Skip to content

Commit 4b31e40

Browse files
authored
Merge pull request #7 from codeclassroom/dev
0.4 release
2 parents a796b3f + 7f1aabc commit 4b31e40

File tree

12 files changed

+172
-86
lines changed

12 files changed

+172
-86
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# Changelog
22

33

4+
## [0.4] - March 10, 2020
5+
6+
### Changed [⚠️ Breaking Changes]
7+
- `getShareScores` & `getInsights` have been decoupled from the check class, they now have to imported separately.
8+
- Minor changes in the `analyze.py` module.
9+
10+
411
## [0.3] - Jan 1, 2020
512

613
### Added

demo.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
"""Usage example"""
22
import os
33
import pprint
4-
from plagcheck import plagcheck
4+
from plagcheck.plagcheck import check, insights, share_scores
55

66
from dotenv import load_dotenv
77
load_dotenv()
88

9-
language = "python"
9+
language = "java"
1010
userid = os.environ["USER_ID"]
1111

1212

13-
moss = plagcheck.check(language, userid)
13+
moss = check(language, userid)
1414

15-
moss.addFilesByWildCard("testfiles/test_python*.py")
15+
moss.addFilesByWildCard("testfiles/test_java*.java")
1616

1717
# or moss.addFile("testfiles/test_python.py")
1818

1919
moss.submit()
2020

2121
print(moss.getHomePage())
22-
pprint.pprint(moss.getResults())
23-
# print frequency of each shared solution
24-
pprint.pprint(moss.getShareScores())
22+
23+
result = moss.getResults()
24+
25+
pprint.pprint(result)
26+
2527
# print potential distributor-culprit relationships
26-
pprint.pprint(moss.getInsights())
28+
pprint.pprint(insights(result))
29+
# print frequency of each shared solution
30+
pprint.pprint(share_scores(result))

docs/changelog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# Changelog
22

33

4+
## [0.4] - March 10, 2020
5+
6+
### Changed [⚠️ Breaking Changes]
7+
- `getShareScores` & `getInsights` have been decoupled from the check class, they now have to imported separately.
8+
- Minor changes in the `analyze.py` module.
9+
10+
411
## [0.3] - Jan 1, 2020
512

613
### Added

docs/insights.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Insights
2+
3+
PlagCheck provides algorithmic analysis of Moss results.
4+
5+
### Terminologies
6+
7+
### 1. Node
8+
Nodes are results returned by Moss i.e every
9+
individual file.
10+
11+
### 2. Tags
12+
Tags are roles which a file serves i.e. a tag is
13+
a potential distributor or potential culprit or
14+
both.
15+
16+
### 3. M-group
17+
m-groups (moss-groups) are groups of solution which have similar code.
18+
For example A student who solves a programming problem may share their
19+
solution with 3 of his/her friends, that is a single m-group with 4 nodes.
20+
21+
For example if you run [demo.py](https://github.com/codeclassroom/PlagCheck/blob/master/demo.py), `insights()` will return the following data:
22+
```java
23+
24+
{'DCtoC Paths': [('testfiles/test_java5.java', 'testfiles/test_java2.java'),
25+
('testfiles/test_java4.java', 'testfiles/test_java2.java')],
26+
'DtoC Paths': [('testfiles/test_java3.java', 'testfiles/test_java2.java'),
27+
('testfiles/test_java3.java', 'testfiles/test_java.java'),
28+
('testfiles/test_java7.java', 'testfiles/test_java6.java')],
29+
'DtoDC Paths': [('testfiles/test_java3.java', 'testfiles/test_java5.java'),
30+
('testfiles/test_java3.java', 'testfiles/test_java4.java')]}
31+
32+
```
33+
34+
This analysis can be visualized into following _Disconnected Directed Graph_
35+
36+
![moss results](https://drive.google.com/uc?export=view&id=1Lc8obgjihfo7EGimn300mTtqfmHK0Zem)
37+
38+
We assign Tags to every individual Node.
39+
40+
1. D - Distributor
41+
Student(s) who distributed their
42+
code in a group.
43+
2. C - Culprit
44+
Student(s) who copied the shared
45+
code.
46+
3. DC - Both a Distributor & Culprit
47+
48+
In the above depicted graph, there are 2 unique _m-groups_.
49+
50+
1. Group 1 : [1, 2, 3, 4, 5]
51+
2. Group 2 : [7, 6]

docs/installation.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,24 @@
22

33
Installing plagcheck is pretty simple, just run
44

5-
`pip install plagcheck`
5+
```bash
6+
pip install plagcheck
7+
```
68

79
Install a specific verison
810

9-
`pip install plagcheck==0.2`
11+
```bash
12+
pip install plagcheck==0.4
13+
```
1014

1115
or directly from GitHub if you cannot wait to test new features
1216

13-
`pip install git+https://github.com/codeclassroom/PlagCheck.git`
17+
```bash
18+
pip install git+https://github.com/codeclassroom/PlagCheck.git
19+
```
1420

15-
If you have already installed it and want to update
21+
If you have a old version, update it using
1622

17-
`pip install --upgrade plagcheck`
23+
```bash
24+
pip install --upgrade plagcheck
25+
```

docs/usage.md

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Usage
22

3-
plagcheck provides the following classes:
3+
plagcheck provides the following classes & methods:
44

55
### check(files, lang, user_id)
66

@@ -16,29 +16,33 @@ plagcheck provides the following classes:
1616
"""Usage example"""
1717
import os
1818
import pprint
19-
from plagcheck import plagcheck
19+
from plagcheck.plagcheck import check, insights, share_scores
2020

2121
from dotenv import load_dotenv
2222
load_dotenv()
2323

24-
language = "python"
24+
language = "java"
2525
userid = os.environ["USER_ID"]
2626

2727

28-
moss = plagcheck.check(language, userid)
28+
moss = check(language, userid)
2929

30-
moss.addFilesByWildCard("testfiles/test_python*.py")
30+
moss.addFilesByWildCard("testfiles/test_java*.java")
3131

3232
# or moss.addFile("testfiles/test_python.py")
3333

3434
moss.submit()
3535

3636
print(moss.getHomePage())
37-
pprint.pprint(moss.getResults())
38-
# print frequency of each shared solution
39-
pprint.pprint(moss.getShareScores())
37+
38+
result = moss.getResults()
39+
40+
pprint.pprint(result)
41+
4042
# print potential distributor-culprit relationships
41-
pprint.pprint(moss.getInsights())
43+
pprint.pprint(insights(result))
44+
# print frequency of each shared solution
45+
pprint.pprint(share_scores(result))
4246

4347
```
4448

@@ -72,18 +76,6 @@ c.getHomePage()
7276
```python
7377

7478
c.getResults()
75-
"""
76-
[
77-
{
78-
"file1":"filename1.py",
79-
"file2":"filename2.py",
80-
"percentage": 34,
81-
"no_of_lines_matched": 3,
82-
"lines_matched":[["2-3", "10-11"]]
83-
},
84-
....
85-
]
86-
"""
8779

8880
```
8981

@@ -162,14 +154,16 @@ program code that also appears in the base file is not counted in matches.
162154
code for an assignment. Multiple Base files are allowed.
163155
- You should use a base file if it is convenient; base files improve results, but are not usually necessary for obtaining useful information.
164156

165-
### 7. getShareScores()
166-
**Parameters** : `None` <br>
157+
<hr>
158+
159+
### share_scores()
160+
**Parameters** : `Moss Results`(returned by `getResults()`) <br>
167161
**Return Type** : `Dict` <br>
168162
**Description**: Share Score is a utility which returns frequency of every individual file.<br>
169163
**Demo**:
170164
```python
171165

172-
c.getShareScores()
166+
print(share_scores(moss_data))
173167

174168
# Will return
175169
"""
@@ -179,4 +173,15 @@ c.getShareScores()
179173
"""
180174
```
181175
Share Score is basically the frequency of each file appearing in Moss Results.
182-
i.e Higher the frequency, the more is that solution "shared" by different files.
176+
i.e Higher the frequency, the more is that solution "shared" by different files.
177+
178+
### insights()
179+
**Parameters** : `Moss Results`(returned by `getResults()`) <br>
180+
**Return Type** : `Dict` <br>
181+
**Description**: See [Insights](/insights).<br>
182+
**Demo**:
183+
```python
184+
185+
print(insights(moss_data))
186+
187+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ nav:
88
- Documentation: index.md
99
- Installation: installation.md
1010
- Usage: usage.md
11+
- PlagCheck Insights: insights.md
1112
- Moss: moss.md
1213
- Changelog: changelog.md
1314
- About: about.md

plagcheck/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""The MOSS interface package for CodeClassroom"""
2-
from plagcheck.plagcheck import check
2+
from plagcheck.plagcheck import check, insights, share_scores

plagcheck/analyze.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __init__(self):
2828
self.nodes = []
2929
self.nodeCount = 0
3030

31-
def relatesTo(self, P1, P2, node1, node2):
31+
def relate(self, P1, P2, node1, node2):
3232
"""Set a path between two file nodes"""
3333
node_obj_dict = {}
3434

plagcheck/plagcheck.py

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,45 @@ def request(url: str):
4343
return req.decode("utf-8")
4444

4545

46+
def share_scores(moss_data: dict) -> dict:
47+
"""Share Score Insights"""
48+
similar_code_files = []
49+
for result in moss_data:
50+
similar_code_files.append(result["file1"])
51+
similar_code_files.append(result["file2"])
52+
53+
# frequency of files which are similar
54+
share_score = collections.Counter(similar_code_files)
55+
56+
return dict(share_score)
57+
58+
59+
def insights(moss_data: dict) -> dict:
60+
"""Analysis for Moss"""
61+
mg = Mgroups()
62+
similar_code_files = set()
63+
insights = {}
64+
65+
for r in moss_data:
66+
similar_code_files.add(r["file1"])
67+
similar_code_files.add(r["file2"])
68+
69+
mg.createNodes(similar_code_files)
70+
71+
for r in moss_data:
72+
mg.relate(
73+
r["percentage_file1"], r["percentage_file2"], r["file1"], r["file2"]
74+
)
75+
76+
mg.set_tags()
77+
78+
insights["DtoC Paths"] = mg.d2c()
79+
insights["DtoDC Paths"] = mg.d2dc()
80+
insights["DCtoC Paths"] = mg.dc2c()
81+
82+
return insights
83+
84+
4685
class check:
4786
"""
4887
Args:
@@ -133,40 +172,3 @@ def getResults(self) -> Tuple[str, Results]:
133172
"""Return the result as a list of dictionary"""
134173

135174
return self.moss_results
136-
137-
def getShareScores(self):
138-
"""Share Score Insights"""
139-
similar_code_files = []
140-
for result in self.moss_results:
141-
similar_code_files.append(result["file1"])
142-
similar_code_files.append(result["file2"])
143-
144-
# frequency of files which are similar
145-
share_score = collections.Counter(similar_code_files)
146-
147-
return dict(share_score)
148-
149-
def getInsights(self):
150-
"""Analysis for Moss"""
151-
mg = Mgroups()
152-
similar_code_files = set()
153-
insights = {}
154-
155-
for r in self.moss_results:
156-
similar_code_files.add(r["file1"])
157-
similar_code_files.add(r["file2"])
158-
159-
mg.createNodes(similar_code_files)
160-
161-
for r in self.moss_results:
162-
mg.relatesTo(
163-
r["percentage_file1"], r["percentage_file2"], r["file1"], r["file2"]
164-
)
165-
166-
mg.set_tags()
167-
168-
insights["DtoC Paths"] = mg.d2c()
169-
insights["DtoDC Paths"] = mg.d2dc()
170-
insights["DCtoC Paths"] = mg.dc2c()
171-
172-
return insights

0 commit comments

Comments
 (0)