duplikacja i błędy danych ze znacznika 'lokalnyId'

0

Witam,

Potrzebuje wydobyć treść znacznika lokalnyId oraz sprawdzić stan składni w przypadku syntax error
z (przykładowej) składni pliku xml.


<gml:FeatureCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:ot="urn:gugik:specyfikacje:gmlas:bazaDanychObiektowTopograficznych10k:2.0" gml:id="GUGiK_OT_KUMN_A" xsi:schemaLocation="urn:gugik:specyfikacje:gmlas:bazaDanychObiektowTopograficznych10k:2.0 ../XSD/BDOT10k_BDOO.xsd">
	<gml:featureMember>
		<ot:OT_KUMN_A gml:id="id_272D6AAF-DB0F-9B0E-E053-CC2BA8C0B5EA">
			<ot:lokalnyId>272D6AAF-DB0F-9B0E-E053-CC2BA1testPB</ot:lokalnyId>
			<ot:przestrzenNazw>PL.PZGiK.337.BDOT10k</ot:przestrzenNazw>
			<ot:wersja>2015-07-30T00:00:00</ot:wersja>
			<ot:poczatekWersjiObiektu>2015-07-30T00:00:00</ot:poczatekWersjiObiektu>
			<ot:oznaczenieZmiany>...</ot:oznaczenieZmiany>
			<ot:zrodloDanychGeometrycznych>...</ot:zrodloDanychGeometrycznych>
			<ot:geometria>
				 gml:id="id-e597f8e9-a1cd-458e-9442-c32a7e4fc280-0" srsName="EPSG:2180" srsDimension="2">
					<gml:exterior>
						<gml:LinearRing>
							<gml:posList>279700.74 397225.41 ... 279655.74 397225.41 279655.74 397225.41</gml:posList>
						</gml:LinearRing>
					</gml:exterior>
				</gml:Polygon>
			</ot:geometria>
		</ot:OT_KUMN_A>
	</gml:featureMember>
	

Z takiego pliku mam dwa przypadki do wykonania:

  1. Sprawdzenie walidacji.
    Dzieje się to kodem:
 plikGML = pliki[int(task.description())]
 try:
            walidowanyPlik = lxml.etree.parse(plikGML)
            nazwa = plikGML[-13:-4]
            print (nazwa)
            unique_lokalnyId = set()
            plikiZparsowane.append(plikGML) # przechowujemy pliki, które są na tyle dobre by wykonać ew. kontrole atrybutowe
            ns = {'bt': 'urn:gugik:specyfikacje:gmlas:modelPodstawowy:1.0'}
            try:
                if walidowanyPlik.xpath('//*[namespace-uri()="%s"]' % ns['bt'], namespaces=ns):
                    raise OSError("Plik nie jest zgodny z obowiązującym schematem aplikacyjnym GML - z plikiem XSD. Plik wykorzystuje przestrzeń nazw Modelu Podstawowego, która była wykorzystywana w schematach aplikacyjnych przed 2021 rokiem.")
            except:
                pass
            root = walidowanyPlik.getroot()
            if root != None:
                wynikWalidacji = xmlschema.validate(walidowanyPlik)
                if not wynikWalidacji:
                    walidacjaZWynikiemPozytywnym = False
                    element=walidowanyPlik.iter()
                    elements_list = list(element)
                    zakresy = []
                    for element in elements_list:
                        if element.tag.endswith('featureMember'):
                            start_line = element.sourceline
                            end_line = element.getnext().sourceline if element.getnext() else float('inf')
                            zakresy.append((start_line, end_line))
                    for error in xmlschema.error_log:
                        line_number = error.line

                        if line_number:
                            found_in_range = False
                            for start_line, end_line in zakresy:
                                if start_line <= line_number <= end_line:
                                    found_in_range = True
                                    for element in elements_list:
                                        if start_line <= element.sourceline <= end_line:
                                            ot_elements = element.xpath('.//*[contains(name(), "lokalnyId")]', namespaces=ns)
                                            for ot_element in ot_elements:
                                                if ot_element.sourceline and ot_element.sourceline <= line_number: # błąd jest poniżej
                                                    lokalnyId = ot_element.text
                                                    if lokalnyId:
                                                        unique_lokalnyId.add(lokalnyId)
                                                        break
                                                elif ot_element.sourceline > line_number: # błąd linii jest poniżej
                                                    lokalnyId = ot_element.text
                                                    if lokalnyId:
                                                        unique_lokalnyId.add(lokalnyId)
                                                        break
                                            break

                    print("Unikalne lokalne ID:", unique_lokalnyId) #typ set

Finalne unique_lokalnyId jest prawidłowy i trafia do:

return {'taskID':task.description(), 'plikGML':plikGML, 'error':xmlschema.error_log, "lokalnyId":unique_lokalnyId}

, ale potrzebuje z tego seta wydobyć tak, by każdy wpisywał się oddzielnie do kolumny excela lub pdf-a odpowiadającemu wierszowi dla 3 pozostałych wartości w return
w tym celu szef mój napisał coś takiego:

 for i in range(len(value['error'])): #value to powyższy return, gdyż jest to w oddzielnej funkcji 
                    # for j in value['lokalnyId']:
                    #     lokalnyId_df.append(str(j))
                    opisBledu = value['error'][i].message
                    for key in frazy:
                        opisBledu = opisBledu.replace(key,frazy[key]) # tłumaczenie- tablica powyżej
                    msg = 'Walidacja pliku: ' + str(value['plikGML']) + ' z wynikiem negatywnym.\n' + \
                          '- ' + config['KodyWalidacji'][value['error'][i].type_name] + '\n' + \
                          '- wiersz: ' + str(value['error'][i].line) + '\n' + \
                          '- komunikat błędu: ' + opisBledu + '\n\n'
                    if formatPlikuRaportu == 'txt':
                        plikRaportu.write(msg)
                    else:
                        walidowanePliki_df.append(str(value['plikGML']))
                        wiersze_df.append(str(value['error'][i].line))
                        opisyBledow_df.append(config['KodyWalidacji'][value['error'][i].type_name])
                        komunikatyBledow_df.append(opisBledu)
                        lokalnyId_df.append(str(value['lokalnyId']))

a ja muszę dodać dodać do tego kodu jeszcze wartości z unique_lokalnyId (set) lub lokalnyId, aby prawidłowo dodawały się do tablicy:

bledyWalidacji = {'WALIDOWANY PLIK': walidowanePliki_df, 'WIERSZ': wiersze_df, 'OPIS BŁĘDU': opisyBledow_df, 'KOMUNIKAT BŁĘDU': komunikatyBledow_df, 'LOKALNY ID OBIEKTU':lokalnyId_df} wykorzystywanej później do tworzenia raportu w excelu i adobe.

Obecnie w pierwszej wyświetlają się nieprawidłowo (dublowanie ostatniej wartości seta), a drugiej nie pozwala z powodu różnic długości danych

  1. Obsługa przypadku (również ze wpisaniem lokalnyId)
    except lxml.etree.XMLSyntaxError as error: oraz except OSError as error:
    Tutaj Gemini AI zaproponowało mi sprawdzanie z patternu:
except lxml.etree.XMLSyntaxError as error:
    error_message = str(error)  # zmiana typu błędu na string
    walidacjaZWynikiemPozytywnym = False
    print (error_message)
    first_digit = re.search(r'\d', error_message).group()
    # Wydobycie linii z frazy
    line_numbers = re.findall(r'line (\d+)', error_message)
    line_number = int(line_numbers[0]) if line_numbers else None
    print("Numer linii:", line_number)

    if line_number:
        # Odczytanie zawartości pliku GML (np. 'plik.gml')
        with open(plikGML, 'r') as file:
            gml_content = file.readlines()

        # Wyszukiwanie najbliższego lokalnyId
        lokalnyId = None
        i = line_number - 1
        pattern = re.compile(r'(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})')
        while lokalnyId is None and i >= 0:
            match = pattern.search(gml_content[i])
            if match:
                lokalnyId = match.group(1)
            i -= 1

        # Szukanie lokalnyId w dół od linii z błędem (jeśli nie znaleziono wcześniej)
        if not lokalnyId:
            for i in range(line_number, len(gml_content)):
                match = pattern.search(gml_content[i])
                if match:
                    lokalnyId = match.group(1)
                    break

        # Sprawdzenie, czy lokalnyId znajduje się w znacznikach <ot:lokalnyId>
        if lokalnyId and not lokalnyId.startswith('<ot:lokalnyId>') and not lokalnyId.endswith('</ot:lokalnyId>'):
            lokalnyId = None

        print("Znaleziony lokalnyId:", lokalnyId)

        return {'taskID':task.description(), 'plikGML':plikGML, 'error':error, "lokalnyId":lokalnyId}


tyle że podaje błędne, bo jak można spojrzeć na strukturze załączonej na początku pattern (\w{8}-\w{4}-\w{4}-\w{4}-\w{12}) występuje dwukrotnie, co spowodowało konieczność rozbicia na przypadki (moja robota):

with open(plikGML, 'r') as file:
                    gml_content = file.readlines()
                i = line_number - 1
                pattern = re.compile(r'(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})')
                pattern_fm = re.compile(r'[<^]gml:featureMember[^>]')
                pattern_geometria_start = re.compile(r'<[^>]*:geometria>')
                pattern_geometria_end = re.compile(r'</[^>]*:geometria>')
                pattern_partial_geometria = re.compile(r'<gml:(Polygon|Point|Curve)[^>]', re.IGNORECASE)
                inside_fm = False
                inside_geometria = False
                lokalnyId = None
                while lokalnyId is None and i >= 0:
                # Szukanie lokalnyId w górę od linii z błędem
                #for i in range(line_number - 1, -1, -1):
                    if pattern_geometria_start.search(gml_content[i]) or pattern_partial_geometria.search(gml_content[i]):
                        inside_geometria = True
                    if pattern_geometria_end.search(gml_content[i]):
                        inside_geometria = False
                    # if not inside_geometria:
                    # if pattern_fm.search(gml_content[i]):
                    #     inside_fm = True
                    
                    # elif pattern_fm.search(gml_content[i]) and not inside_fm:
                    #     break
                    if not inside_geometria: # not inside_fm and 
                        match = pattern.search(gml_content[i])
                        if match:
                            lokalnyId = match.group(1)
                            print ("traf", lokalnyId)
                            break
                    i -= 1

                # Szukanie lokalnyId w dół od linii z błędem (jeśli nie znaleziono wcześniej)
                if not lokalnyId:
                    for i in range(line_number, len(gml_content)):
                        if pattern_geometria_end.search(gml_content[i]):
                            inside_geometria = False
                        if pattern_geometria_start.search(gml_content[i]) or pattern_partial_geometria.search(gml_content[i]):
                            inside_geometria = True
                        if not inside_geometria:
                        # if pattern_fm.search(gml_content[i]):
                        #     inside_fm = True
                        # elif pattern_fm.search(gml_content[i]) and not inside_fm:
                        #     break
                        # if not inside_fm:
                            match = pattern.search(gml_content[i])
                            if match:
                                lokalnyId = match.group(1)
                                print ("znal", lokalnyId)

czyli żeby w przypadku znalezienia błędu składniowego wewnątrz 'geometria' (przedrostki jak tutaj ot: mogą być różne!) szukał w górę, podobnie jeśli znajdzie błąd poniżej niego, ale powyżej gml:featureMember (tutaj już stały przedrostek)-> W skrócie chodzi, by w przypadku dowolnego błędu synthax (składniowego) pliku xml ma szukać lokalnegoId wewnątrz danego gml:featureMember (jeśli w tej frazie też jest błąd to między poprzednim poprawnym, a następnym).

Z góry dziękuje za wszelką pomoc!

PS. znacznik (przykładowy tutaj) OT_KUMN_A zmienia się w zależności od nazwy pliku (ostatnie 9 znaków)!

1

Ale żeś tego dowalił, spróbuj wyizolować najmniejszy możliwy kawałek kodu z błędem

0

Jeśli to jest skomplikowane, długie i nie zrozumiałe to proszę na podstawie ząłączonego fragmentu pliku xml podać sposób na wydobycie wartości znacznika lokalnyId, także gdy jest syntax error, wewnątrz danego gml:featureMember

0
lion137 napisał(a):

Ale żeś tego dowalił, spróbuj wyizolować najmniejszy możliwy kawałek kodu z błędem

Dla Syntax.error zmieniłem przykład:

funkcja (ta zaczynająca się od with open(plikGML, 'r') as file: ) wywala e597f8e9-a1cd-458e-9442-c32a7e4fc280 zamiast 272D6AAF-DB0F-9B0E-E053-CC2BA1testPB. Nie zależnie od miejsca błędu składni ma szukać tego pierwszego spełniającego dany pattern i będący wewnątrz lokalnyId, a nie gml:id

0

Ale zakładasz, że błąd będzie w strukturze pliku ‘xml’ czy w nazwie znacznika <ot:lokalnyId>?
Bo można by szukać tego znacznika, skoro zmieniłeś szablon wartości w tym znaczniku.

0

Zakładam że błąd może być w każdym znaczniku włącznie z tym, dlatego ważniejsze jest szukanie po frazie lokalnyId. W tym celu AI poleciło mi pattern:

pattern = re.compile(r'(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})')

oraz frazy:

pattern_geometria_start = re.compile(r'<[^>]*:geometria>')
pattern_geometria_end = re.compile(r'</[^>]*:geometria>')
pattern_partial_geometria = re.compile(r'<gml:(Polygon|Point|Curve)[^>]')
0

Jeżeli znacznik <ot:lokalnyId> nie uległ uszkodzeniu, to najprościej wyszukać po tym znaczniku i sprawdzić poprawność zawartości według maski, ewentualnie wykonać procedurę naprawczą zawartości na podstawie cech (tylko duże litery i cyfry oraz znak ‛-’ ułożone w odpowiedni sposób).
W przeciwnym przypadku można zlokalizować graniczne znaczniki i wydobyć z tego fragmentu zawartość znacznika localnyId według maski.
Można też po zlokalizowaniu granicznych znaczników zlokalizować granice uszkodzonego znacznika localnyId i sprawdzić poprawność według maski, ewentualnie wykonać procedurę naprawczą.

1 użytkowników online, w tym zalogowanych: 0, gości: 1