Skip to content
This repository was archived by the owner on Aug 25, 2024. It is now read-only.

Commit 5266361

Browse files
author
John Andersen
committed
docs: Complete integration example
Signed-off-by: John Andersen <[email protected]>
1 parent 0c2ee86 commit 5266361

File tree

5 files changed

+311
-1
lines changed

5 files changed

+311
-1
lines changed

docs/usage/integration.rst

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,5 +439,150 @@ Modifying the Legacy App
439439
440440
Now let's add a 'Predict' button to the app. The button will trigger the
441441
operations to be run on the new URL, and then the prediction. The demo app will
442-
then take the predicted classification and rcord that as the classification in
442+
then take the predicted classification and record that as the classification in
443443
the database.
444+
445+
**examples/maintained/index.html**
446+
447+
.. code-block:: html
448+
449+
<div>
450+
<p>Submit or change the maintenance status of a repo.</p>
451+
<input id="URL" placeholder="git://server.git/repo"></input>
452+
<button id="maintained" class="good">Maintained</button>
453+
<button id="unmaintained" class="dangerous">Unmaintained</button>
454+
<button id="predict">Predict</button>
455+
</div>
456+
457+
The predict button will trigger an HTTP call to the CGI API. The CGI script will
458+
run the same commands we just ran on the command line, and parse the output,
459+
which is in JSON format, and set the resulting prediction classification in the
460+
database.
461+
462+
**examples/maintained/cgi-bin/api.py**
463+
464+
.. code-block:: python
465+
466+
# Add the imports we'll be using to the top of api.py
467+
import subprocess
468+
from datetime import datetime
469+
470+
# ...
471+
472+
elif action == 'predict':
473+
today = datetime.now().strftime('%Y-%m-%d %H:%M')
474+
operations = [
475+
'group_by',
476+
'quarters_back_to_date',
477+
'check_if_valid_git_repository_URL',
478+
'clone_git_repo',
479+
'git_repo_default_branch',
480+
'git_repo_checkout',
481+
'git_repo_commit_from_date',
482+
'git_repo_author_lines_for_dates',
483+
'work',
484+
'git_repo_release',
485+
'git_commits',
486+
'count_authors',
487+
'cleanup_git_repo'
488+
]
489+
subprocess.check_call(([
490+
'dffml', 'operations', 'repo',
491+
'-log', 'debug',
492+
'-sources', 'db=demoapp',
493+
'-update',
494+
'-keys', query['URL'],
495+
'-repo-def', 'URL',
496+
'-remap',
497+
'group_by.work=work',
498+
'group_by.commits=commits',
499+
'group_by.authors=authors',
500+
'-dff-memory-operation-network-ops'] + operations + [
501+
'-dff-memory-opimp-network-opimps'] + operations + [
502+
'-inputs'] + \
503+
['%d=quarter' % (i,) for i in range(0, 10)] + [
504+
'\'%s\'=quarter_start_date' % (today,),
505+
'True=no_git_branch_given',
506+
'-output-specs', '''{
507+
"authors": {
508+
"group": "quarter",
509+
"by": "author_count",
510+
"fill": 0
511+
},
512+
"work": {
513+
"group": "quarter",
514+
"by": "work_spread",
515+
"fill": 0
516+
},
517+
"commits": {
518+
"group": "quarter",
519+
"by": "commit_count",
520+
"fill": 0
521+
}
522+
}=group_by_spec''']))
523+
result = subprocess.check_output([
524+
'dffml', 'predict', 'repo',
525+
'-keys', query['URL'],
526+
'-model', 'dnn',
527+
'-sources', 'db=demoapp',
528+
'-classifications', '0', '1',
529+
'-features',
530+
'def:authors:int:10',
531+
'def:commits:int:10',
532+
'def:work:int:10',
533+
'-log', 'critical',
534+
'-update'])
535+
result = json.loads(result)
536+
cursor.execute("REPLACE INTO status (src_url, maintained) VALUES(%s, %s)",
537+
(query['URL'], result[0]['prediction']['classification'],))
538+
cnx.commit()
539+
print json.dumps(dict(success=True))
540+
541+
Hook up the predict button to call our new API.
542+
543+
**examples/maintained/ml.js**
544+
545+
.. code-block:: javascript
546+
547+
function predict(URL) {
548+
return fetch('cgi-bin/api.py?action=predict' +
549+
'&maintained=' + Number(maintained) +
550+
'&URL=' + URL)
551+
.then(function(response) {
552+
return response.json()
553+
}.bind(this));
554+
}
555+
556+
window.addEventListener('DOMContentLoaded', function(event) {
557+
var tableDOM = document.getElementById('table');
558+
var URLDOM = document.getElementById('URL');
559+
var predictDOM = document.getElementById('predict');
560+
561+
predictDOM.addEventListener('click', function(event) {
562+
predict(URLDOM.value)
563+
.then(function() {
564+
refreshTable(tableDOM);
565+
});
566+
});
567+
});
568+
569+
Finally, import the new script into the main page.
570+
571+
**examples/maintained/index.html**
572+
573+
.. code-block:: html
574+
575+
<head>
576+
<title>Git Repo Maintenance Tracker</title>
577+
<script type="text/javascript" src="main.js"></script>
578+
<script type="text/javascript" src="ml.js"></script>
579+
<link rel="stylesheet" type="text/css" href="theme.css">
580+
</head>
581+
582+
Visit the web app, and input a Git repo to evaluate into the input field. Then
583+
click predict. The demo gif has had all the entries DROPed from the database.
584+
But you can look through the table and you'll find that a prediction has been
585+
run on the repo. Congratulations! You've automated a manual classification
586+
process, and integrated machine learning into a legacy application.
587+
588+
.. image:: /images/integration_demo.gif
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env python2
2+
import os
3+
import cgi
4+
import sys
5+
import json
6+
import urlparse
7+
import subprocess
8+
import mysql.connector
9+
from datetime import datetime
10+
11+
from conf import MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, MYSQL_PORT
12+
13+
print 'Content-Type: application/json'
14+
print ''
15+
16+
query = dict(urlparse.parse_qsl(os.getenv('QUERY_STRING', default='')))
17+
18+
action = query.get('action', None)
19+
20+
if action is None:
21+
print json.dumps(dict(error='Need method query parameter'))
22+
sys.exit(1)
23+
24+
cnx = mysql.connector.connect(
25+
user=MYSQL_USER,
26+
passwd=MYSQL_PASSWORD,
27+
database=MYSQL_DATABASE,
28+
port=MYSQL_PORT
29+
)
30+
cursor = cnx.cursor()
31+
32+
if action == 'dump':
33+
cursor.execute("SELECT src_url, maintained FROM status")
34+
print json.dumps(dict(cursor))
35+
elif action == 'set':
36+
cursor.execute("REPLACE INTO status (src_url, maintained) VALUES(%s, %s)",
37+
(query['URL'], query['maintained'],))
38+
cnx.commit()
39+
print json.dumps(dict(success=True))
40+
elif action == 'predict':
41+
today = datetime.now().strftime('%Y-%m-%d %H:%M')
42+
operations = [
43+
'group_by',
44+
'quarters_back_to_date',
45+
'check_if_valid_git_repository_URL',
46+
'clone_git_repo',
47+
'git_repo_default_branch',
48+
'git_repo_checkout',
49+
'git_repo_commit_from_date',
50+
'git_repo_author_lines_for_dates',
51+
'work',
52+
'git_repo_release',
53+
'git_commits',
54+
'count_authors',
55+
'cleanup_git_repo'
56+
]
57+
subprocess.check_call(([
58+
'dffml', 'operations', 'repo',
59+
'-log', 'debug',
60+
'-sources', 'db=demoapp',
61+
'-update',
62+
'-keys', query['URL'],
63+
'-repo-def', 'URL',
64+
'-remap',
65+
'group_by.work=work',
66+
'group_by.commits=commits',
67+
'group_by.authors=authors',
68+
'-dff-memory-operation-network-ops'] + operations + [
69+
'-dff-memory-opimp-network-opimps'] + operations + [
70+
'-inputs'] + \
71+
['%d=quarter' % (i,) for i in range(0, 10)] + [
72+
'\'%s\'=quarter_start_date' % (today,),
73+
'True=no_git_branch_given',
74+
'-output-specs', '''{
75+
"authors": {
76+
"group": "quarter",
77+
"by": "author_count",
78+
"fill": 0
79+
},
80+
"work": {
81+
"group": "quarter",
82+
"by": "work_spread",
83+
"fill": 0
84+
},
85+
"commits": {
86+
"group": "quarter",
87+
"by": "commit_count",
88+
"fill": 0
89+
}
90+
}=group_by_spec''']))
91+
result = subprocess.check_output([
92+
'dffml', 'predict', 'repo',
93+
'-keys', query['URL'],
94+
'-model', 'dnn',
95+
'-sources', 'db=demoapp',
96+
'-classifications', '0', '1',
97+
'-features',
98+
'def:authors:int:10',
99+
'def:commits:int:10',
100+
'def:work:int:10',
101+
'-log', 'critical',
102+
'-update'])
103+
result = json.loads(result)
104+
cursor.execute("REPLACE INTO status (src_url, maintained) VALUES(%s, %s)",
105+
(query['URL'], result[0]['prediction']['classification'],))
106+
cnx.commit()
107+
print json.dumps(dict(success=True))
108+
else:
109+
print json.dumps(dict(error='Unknown action'))
110+
111+
sys.stdout.flush()
112+
cursor.close()
113+
cnx.close()

examples/maintained/cgi-bin/api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@
3838
else:
3939
print json.dumps(dict(error='Unknown action'))
4040

41+
sys.stdout.flush()
4142
cursor.close()
4243
cnx.close()

examples/maintained/ml.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Git Repo Maintenance Tracker</title>
5+
<script type="text/javascript" src="main.js"></script>
6+
<script type="text/javascript" src="ml.js"></script>
7+
<link rel="stylesheet" type="text/css" href="theme.css">
8+
</head>
9+
<body>
10+
<h1>Git Repo Maintenance Tracker</h1>
11+
12+
<hr />
13+
14+
<div>
15+
<p>Submit or change the maintenance status of a repo.</p>
16+
<input id="URL" placeholder="git://server.git/repo"></input>
17+
<button id="maintained" class="good">Maintained</button>
18+
<button id="unmaintained" class="dangerous">Unmaintained</button>
19+
<button id="predict">Predict</button>
20+
</div>
21+
22+
<br />
23+
24+
<div>
25+
<table id="table" style="width:100%">
26+
</table>
27+
</div>
28+
29+
</body>
30+
</html>

examples/maintained/ml.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
function predict(URL) {
2+
return fetch('cgi-bin/api-ml.py?action=predict' +
3+
'&maintained=' + Number(maintained) +
4+
'&URL=' + URL)
5+
.then(function(response) {
6+
return response.json()
7+
}.bind(this));
8+
}
9+
10+
window.addEventListener('DOMContentLoaded', function(event) {
11+
var tableDOM = document.getElementById('table');
12+
var URLDOM = document.getElementById('URL');
13+
var predictDOM = document.getElementById('predict');
14+
15+
predictDOM.addEventListener('click', function(event) {
16+
predict(URLDOM.value)
17+
.then(function() {
18+
refreshTable(tableDOM);
19+
});
20+
});
21+
});

0 commit comments

Comments
 (0)