File size: 10,413 Bytes
dcce181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
from feature_extraction import extract_all_features


# ─────────────────────────────────────────────────────────────────────────────
# UČITAVANJE MODELA
# ─────────────────────────────────────────────────────────────────────────────

MODEL_NAME = "Salesforce/codegen-350M-mono"


def ucitaj_model():
   
    try:
        from transformers import AutoTokenizer, AutoModelForCausalLM
        import torch

        print(f"  Učitavam model '{MODEL_NAME}'...")
        print("  (Pri prvom pokretanju ovo moΕΎe potrajati par minuta.)\n")

        tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
        model     = AutoModelForCausalLM.from_pretrained(
            MODEL_NAME,
            torch_dtype=torch.float32,   # float32 radi na CPU bez GPU-a
        )
        model.eval()   # isključi dropout β€” samo inferenca, ne treniranje

        num_params = sum(p.numel() for p in model.parameters()) // 1_000_000
        print(f"  Model učitan ({num_params}M parametara).\n")
        return model, tokenizer

    except ImportError:
        print("  [UPOZORENJE] transformers ili torch nisu instalirani.")
        print("  Pokreni: pip install transformers torch\n")
        return None, None

    except Exception as e:
        print(f"  [UPOZORENJE] Model nije mogao biti učitan: {e}\n")
        return None, None


# ─────────────────────────────────────────────────────────────────────────────
# UNOS KODA
# ─────────────────────────────────────────────────────────────────────────────

def ucitaj_kod() -> str:
   
    print("Zalijepi kod ispod, a kad zavrΕ‘iΕ‘ pritisni Enter dva puta:\n")
    linije = []
    prazni_zaredom = 0

    while True:
        linija = input()
        if linija == "":
            prazni_zaredom += 1
            if prazni_zaredom >= 2:
                break
            linije.append(linija)
        else:
            prazni_zaredom = 0
            linije.append(linija)

    return "\n".join(linije).strip()


def ucitaj_iz_datoteke(putanja: str) -> str:
    
    try:
        with open(putanja, "r", encoding="utf-8") as f:
            sadrzaj = f.read()
        print(f"  Učitano {len(sadrzaj.splitlines())} linija iz '{putanja}'.")
        return sadrzaj
    except FileNotFoundError:
        print(f"\n  [GREΕ KA] Datoteka '{putanja}' nije pronaΔ‘ena.")
        print(  "  Provjeri je li datoteka u istom folderu kao main.py.")
        return ""
    except Exception as e:
        print(f"\n  [GREŠKA] Problem pri čitanju datoteke: {e}")
        return ""


# ─────────────────────────────────────────────────────────────────────────────
# ISPIS REZULTATA
# ─────────────────────────────────────────────────────────────────────────────

def ispisi_znacajke(features: dict) -> None:
    
    jezik = features.get("detected_language", "nepoznat")
    print(f"\n{'═' * 55}")
    print(f"  Prepoznat jezik: {jezik.upper()}")
    print(f"{'═' * 55}")

    # ── Stilska detekcija ──────────────────────────────────────
    print("\n  [ STILSKA DETEKCIJA β€” komentari, imenovanje, formatiranje ]\n")

    stilske = [
        ("Broj linija s komentarom",        "num_comment_lines"),
        ("Udio komentara (%)",               "comment_ratio"),
        ("Prosj. duljina komentara (rij.)",  "avg_comment_length_words"),
        ("Udio komentara u znakovima",       "comment_to_code_ratio"),
        ("Broj blok komentara",              "num_block_comments"),
        ("Broj docstringova / JSDoc",        "num_docstrings"),
        ("Prosj. duljina identifikatora",    "avg_identifier_length"),
        ("Prosj. duljina naziva funkcija",   "avg_function_name_length"),
        ("Udio jednoslovnih naziva",         "single_char_name_ratio"),
        ("Leksička raznolikost naziva",      "lexical_diversity"),
        ("Udio snake_case stila",            "snake_case_ratio"),
        ("Udio camelCase stila",             "camel_case_ratio"),
        ("Konzistentnost imenovanja",        "naming_consistency"),
        ("Ukupno linija",                    "total_lines"),
        ("Udio praznih linija",              "empty_line_ratio"),
        ("Prosj. duljina linije (znakovi)",  "avg_line_length"),
        ("Max duljina linije",               "max_line_length"),
        ("Koristi tabove (1=da, 0=ne)",      "uses_tabs"),
        ("Udio linija s trailing space",     "trailing_whitespace_ratio"),
        ("Konzistentnost razmaka operatori", "operator_spacing_consistency"),
    ]

    for naziv, kljuc in stilske:
        val = features.get(kljuc, "N/A")
        if isinstance(val, float):
            print(f"    {naziv:<38} {val:.4f}")
        else:
            print(f"    {naziv:<38} {val}")

    # ── Strukturna detekcija ───────────────────────────────────
    print("\n  [ STRUKTURNA DETEKCIJA β€” AST, sloΕΎenost, tok kontrole ]\n")

    strukturne = [
        ("Dubina AST stabla",                "ast_depth"),
        ("Broj čvorova u AST stablu",        "ast_node_count"),
        ("Raznolikost tipova čvorova",       "unique_node_type_ratio"),
        ("Broj funkcija / metoda",           "num_functions"),
        ("Prosj. duljina funkcije (linije)", "avg_function_length"),
        ("Max duljina funkcije (linije)",    "max_function_length"),
        ("Prosj. broj argumenata",           "avg_args_per_function"),
        ("Broj klasa",                       "num_classes"),
        ("Broj importa",                     "num_imports"),
        ("Broj if naredbi",                  "num_if_statements"),
        ("Broj for petlji",                  "num_for_loops"),
        ("Broj while petlji",                "num_while_loops"),
        ("Broj try/catch blokova",           "num_try_blocks"),
        ("Broj lambda izraza",               "num_lambdas"),
        ("Max dubina ugnijeΕΎΔ‘enosti",        "max_nesting_depth"),
        ("Prosj. dubina ugnijeΕΎΔ‘enosti",     "avg_nesting_depth"),
        ("Aproks. ciklomatska sloΕΎenost",    "cyclomatic_complexity_approx"),
    ]

    for naziv, kljuc in strukturne:
        val = features.get(kljuc, "N/A")
        if isinstance(val, float):
            print(f"    {naziv:<38} {val:.4f}")
        else:
            print(f"    {naziv:<38} {val}")

    # ── Statistička detekcija ──────────────────────────────────
    print("\n  [ STATISTIČKA DETEKCIJA β€” perplexity ]\n")

    perp     = features.get("perplexity", -1.0)
    dostupan = features.get("model_available", 0)

    if dostupan == 0:
        print("    Model nije učitan β€” perplexity nije izračunat.")
    else:
        print(f"    {'Perplexity':<38} {perp:.4f}")

        # Grubo tumačenje perplexityja za CodeGEN-350M
        if perp < 5:
            tumacenje = "vrlo nizak β†’ vjerojatno AI"
        elif perp < 20:
            tumacenje = "nizak → moguće AI"
        elif perp < 60:
            tumacenje = "srednji β†’ nejasno"
        else:
            tumacenje = "visok β†’ vjerojatno čovjek"
        print(f"    {'Tumačenje':<38} {tumacenje}")

    print(f"\n{'═' * 55}")
    print(f"  Ukupno izvučeno značajki: {len(features) - 1}")
    print(f"{'═' * 55}\n")


# ─────────────────────────────────────────────────────────────────────────────
# GLAVNI PROGRAM
# ─────────────────────────────────────────────────────────────────────────────

def main():
    print("=" * 55)
    print("  Ekstraktor značajki koda")
    print("  (za detekciju AI generiranog koda)")
    print("=" * 55)

    # Pitaj korisnika želi li učitati model za perplexity
    print("\nŽeliő li učitati model za izračun perplexityja?")
    print("  d β€” Da (skida ~700 MB pri prvom pokretanju)")
    print("  n β€” Ne (perplexity Δ‡e biti preskočen)\n")
    odabir_model = input("Odabir (d/n): ").strip().lower()

    model, tokenizer = None, None
    if odabir_model in ("d", "da", "y", "yes"):
        model, tokenizer = ucitaj_model()

    # Način unosa koda
    print("\nNačin unosa koda:")
    print("  1 β€” Zalijepi kod direktno u terminal")
    print("  2 β€” Učitaj iz datoteke (npr. kod.txt)")

    while True:
        print()
        odabir = input("Odaberi način unosa (1 ili 2): ").strip()

        if odabir == "1":
            kod = ucitaj_kod()

        elif odabir == "2":
            putanja = input("Putanja do datoteke (Enter za 'kod.txt'): ").strip()
            if putanja == "":
                putanja = "kod.txt"
            kod = ucitaj_iz_datoteke(putanja)

        else:
            print("  Unesi 1 ili 2.")
            continue

        if not kod:
            print("  Kod je prazan. PokuΕ‘aj ponovno.")
            continue

        print("\nIzvlačim značajke...")
        znacajke = extract_all_features(kod, model=model, tokenizer=tokenizer)
        ispisi_znacajke(znacajke)

        odgovor = input("Želiő li analizirati joő jedan isječak koda? (da/ne): ")
        if odgovor.strip().lower() not in ("da", "d", "yes", "y"):
            print("\nZatvaram program.")
            break


if __name__ == "__main__":
    main()