WolfgangFahl / pyCEURmake

CEUR make python implementation
Apache License 2.0
2 stars 1 forks source link

add urn checkdigit generator #88

Closed WolfgangFahl closed 10 months ago

WolfgangFahl commented 10 months ago
"""
Created on 2023-12-28

@author: wf / ChatGPT-4 as instructed
"""
class URN:
    """
    URN check digit calculator for DNB URN service:

    see https://www.dnb.de/DE/Professionell/Services/URN-Service/urn-service_node.html

    and
        https://d-nb.info/1045320641/34
        http://nbn-resolving.de/nbnpruefziffer.php

    """
    @classmethod
    def check_urn_checksum(cls,urn:str, debug:bool=False)->bool:
        expected_check_digit = int(urn[-1])
        urn_prefix=urn[:-1]
        check_digit=cls.calc_urn_checksum(urn_prefix, debug)
        return check_digit==expected_check_digit

    @classmethod
    def calc_urn_checksum(cls,test_urn:str, debug:bool=False)->int:
        """
        converted from PHP and JavaScript code see
        see https://github.com/bohnelang/URN-Pruefziffer

        Args:
            debug(bool) if True show the internal values while calculating
        """
        # Code string provided in the original PHP function
        code = '3947450102030405060708094117############1814191516212223242542262713282931123233113435363738########43'

        # Initialization of variables
        _sum = 0
        pos = 1

        # Iterating through each character in the URN
        for i,char in enumerate(test_urn.upper()):
            # Getting the ASCII value and adjusting it based on the character '-' (45 in ASCII)
            x = ord(char) - 45
            # Extracting two consecutive values from the code string
            v1 = int(code[x*2]) if code[x*2] != '#' else 0
            v2 = int(code[x*2+1]) if code[x*2+1] != '#' else 0

            if v1 == 0:
                # If v1 is 0, increment pos after multiplying v2 with its current value
                _sum += v2 * pos
                pos += 1  # post-increment equivalent in Python
            else:
                # If v1 is not 0, use pos for the first term, increment pos, then use the new value of pos for the second term
                # This effectively increases pos by 2 in this branch
                _sum += pos * v1
                pos += 1  # increment for the first term
                _sum += v2 * pos  # use incremented pos for the second term
                pos += 1  # increment for the second term

            if debug:
                print(f"i: {i:2} pos: {pos:2} x: {x:2} v1: {v1:2} v2: {v2:2} sum: {_sum:4}")

        # Assuming v2 is not 0 at the end of your URN calculations
        check_digit = (_sum // v2) % 10  # Using integer division for floor behavior

        return check_digit
WolfgangFahl commented 10 months ago

Javascript with debug output:

# Adjusting the given JavaScript function to include logging for i, x, v1, v2, and sum
js_code_with_logging = """
<!DOCTYPE html>
<html>
<head>
    <title>URN:NBN Check Digit Calculator</title>
</head>
<body>

<h1>URN:NBN Check Digit Calculator</h1>

<form id="urnForm">
    <label for="urnInput">Enter URN (without check digit):</label><br>
    <input type="text" id="urnInput" name="urnInput"><br>
    <input type="button" value="Calculate Check Digit" onclick="calculateCheckDigit()">
</form>

<p id="result"></p>
<p id="log"></p>

<script>
function calc_urn_checksum(test_urn) {
    var code = '3947450102030405060708094117############1814191516212223242542262713282931123233113435363738########43';
    var logOutput = "";
    for (var i = 0, sum = 0, pos = 1; i < test_urn.length; i++) {
        var x = test_urn.toUpperCase().charCodeAt(i) - 45;
        var v1 = code.substr(x * 2, 1);
        var v2 = code.substr(x * 2 + 1, 1);
        sum += (v1 == 0) ? v2 * pos++ : pos++ * v1 + v2 * pos++;

        // Adding logging for i, x, v1, v2, and sum
        logOutput += 'i: ' + i + ',pos: '+ pos + ', x: ' + x + ', v1: ' + v1 + ', v2: ' + v2 + ', sum: ' + sum + '<br>';
    }
    // Adding log output to the HTML
    document.getElementById("log").innerHTML = logOutput;
    return Math.floor(sum / v2) % 10;
}

function calculateCheckDigit() {
    var urn = document.getElementById("urnInput").value;
    var checkDigit = calc_urn_checksum(urn);
    document.getElementById("result").innerHTML = "Calculated Check Digit: " + checkDigit;
}
</script>

</body>
</html>
"""

# Writing to an HTML file
file_path = "/tmp/urn_checker_with_logging.html"
with open(file_path, "w") as file:
    file.write(js_code_with_logging)

file_path
WolfgangFahl commented 10 months ago

PHP with command line

<?php
// Accepting URN from command line argument
if($argc !== 2) {
    echo "Usage: php urn_checker.php [URN]\n";
    exit(1);
}

$test_org = $argv[1];  // Assigning the URN from command line argument
$test_urn = substr($test_org, 0, -1);  // Removing the last digit for testing

// Here: One line of code for calculation...
for (
    $code = "3947450102030405060708094117############1814191516212223242542262713282931123233113435363738########43",
    $i = $sum = 0,
    $pos = 1;
    $i < strlen($test_urn);
    $i++, $sum += ($v1 == 0) ? $v2 * $pos++ : $pos++ * $v1 + $v2 * $pos++,
    $purn = $sum / $v2 % 10
) list($v1, $v2) = array(substr($code, (ord(strtoupper(substr($test_urn, $i, 1))) - 45) * 2, 1), substr($code, (ord(strtoupper(substr($test_urn, $i, 1))) - 45) * 2 + 1, 1));

// Output / Check
echo "Input URN: $test_urn\n";
echo "Calculated Check Digit: $purn\n";
echo "Full URN with Check Digit: ".$test_urn.$purn."\n";
echo "Verification: ".($test_urn.$purn == $test_org ? "OK" : "Error")."\n";
?>
WolfgangFahl commented 10 months ago

Debug output

urn:nbn:de:0074-2195-9
Calculated Check Digit: 9

i: 0,pos: 3, x: 40, v1: 1, v2: 1, sum: 3
i: 1,pos: 5, x: 37, v1: 1, v2: 2, sum: 14
i: 2,pos: 7, x: 33, v1: 1, v2: 3, sum: 37
i: 3,pos: 9, x: 13, v1: 1, v2: 7, sum: 100
i: 4,pos: 11, x: 33, v1: 1, v2: 3, sum: 139
i: 5,pos: 13, x: 21, v1: 1, v2: 4, sum: 198
i: 6,pos: 15, x: 33, v1: 1, v2: 3, sum: 253
i: 7,pos: 17, x: 13, v1: 1, v2: 7, sum: 380
i: 8,pos: 19, x: 23, v1: 1, v2: 5, sum: 487
i: 9,pos: 21, x: 24, v1: 1, v2: 6, sum: 626
i: 10,pos: 23, x: 13, v1: 1, v2: 7, sum: 801
i: 11,pos: 24, x: 3, v1: 0, v2: 1, sum: 824
i: 12,pos: 25, x: 3, v1: 0, v2: 1, sum: 848
i: 13,pos: 26, x: 10, v1: 0, v2: 8, sum: 1048
i: 14,pos: 27, x: 7, v1: 0, v2: 5, sum: 1178
i: 15,pos: 29, x: 0, v1: 3, v2: 9, sum: 1511
i: 16,pos: 30, x: 5, v1: 0, v2: 3, sum: 1598
i: 17,pos: 31, x: 4, v1: 0, v2: 2, sum: 1658
i: 18,pos: 33, x: 12, v1: 4, v2: 1, sum: 1814
i: 19,pos: 34, x: 8, v1: 0, v2: 6, sum: 2012
i: 20,pos: 36, x: 0, v1: 3, v2: 9, sum: 2429

urn:nbn:de:0183-mbi000372
Calculated Check Digit: 1
i: 0,pos: 3, x: 40, v1: 1, v2: 1, sum: 3
i: 1,pos: 5, x: 37, v1: 1, v2: 2, sum: 14
i: 2,pos: 7, x: 33, v1: 1, v2: 3, sum: 37
i: 3,pos: 9, x: 13, v1: 1, v2: 7, sum: 100
i: 4,pos: 11, x: 33, v1: 1, v2: 3, sum: 139
i: 5,pos: 13, x: 21, v1: 1, v2: 4, sum: 198
i: 6,pos: 15, x: 33, v1: 1, v2: 3, sum: 253
i: 7,pos: 17, x: 13, v1: 1, v2: 7, sum: 380
i: 8,pos: 19, x: 23, v1: 1, v2: 5, sum: 487
i: 9,pos: 21, x: 24, v1: 1, v2: 6, sum: 626
i: 10,pos: 23, x: 13, v1: 1, v2: 7, sum: 801
i: 11,pos: 24, x: 3, v1: 0, v2: 1, sum: 824
i: 12,pos: 25, x: 4, v1: 0, v2: 2, sum: 872
i: 13,pos: 26, x: 11, v1: 0, v2: 9, sum: 1097
i: 14,pos: 27, x: 6, v1: 0, v2: 4, sum: 1201
i: 15,pos: 29, x: 0, v1: 3, v2: 9, sum: 1534
i: 16,pos: 31, x: 32, v1: 2, v2: 7, sum: 1802
i: 17,pos: 33, x: 21, v1: 1, v2: 4, sum: 1961
i: 18,pos: 35, x: 28, v1: 2, v2: 4, sum: 2163
i: 19,pos: 36, x: 3, v1: 0, v2: 1, sum: 2198
i: 20,pos: 37, x: 3, v1: 0, v2: 1, sum: 2234
i: 21,pos: 38, x: 3, v1: 0, v2: 1, sum: 2271
i: 22,pos: 39, x: 6, v1: 0, v2: 4, sum: 2423
i: 23,pos: 40, x: 10, v1: 0, v2: 8, sum: 2735
i: 24,pos: 41, x: 5, v1: 0, v2: 3, sum: 2855
WolfgangFahl commented 10 months ago
 Vol-1909   urn:nbn:de:0074-1909-C
Vol-2018    urn:nbn:de:0074-2018-6
Vol-2210    urn:nbn:de:0074-2212-3
Vol-2479    urn:nbn:de:0074-2479-C
Vol-2743    urn:nbn:de:0074-2743-1
Vol-2843    urn:nbn:de:074-Vol-2843-4
Vol-3419    urn:nbn:de:0074-3419-9
Vol-3466    urn:nbn:de:0074-XXX-1