From bc96133c4074e3e98400d8441e763b1e2c66efda Mon Sep 17 00:00:00 2001
From: HenryShan <zshan2@illinois.edu>
Date: Mon, 15 Mar 2021 16:08:01 +0800
Subject: [PATCH] assignment-2.2 ver2.0 CSS style added, creating form input

---
 DataBase/mongoDB.py                   |   3 -
 RegularExpressionParser/Parser.py     |   2 +
 Server/SimpleServer.py                |  36 +++++--
 Tests/ServerTests.py                  |   4 +-
 web/app/src/App.js                    | 134 ++++++++++++++++++++------
 web/app/src/material/sign_correct.png | Bin 0 -> 8706 bytes
 web/app/src/material/sign_error.png   | Bin 0 -> 9611 bytes
 7 files changed, 138 insertions(+), 41 deletions(-)
 create mode 100644 web/app/src/material/sign_correct.png
 create mode 100644 web/app/src/material/sign_error.png

diff --git a/DataBase/mongoDB.py b/DataBase/mongoDB.py
index 1c794be..ef437fc 100644
--- a/DataBase/mongoDB.py
+++ b/DataBase/mongoDB.py
@@ -98,9 +98,6 @@ def get_documents_json(opt, identifier):
     for item in data:
         item.pop("_id")
         file[typeName].append(item)
-    print("===============================================")
-    print(file)
-    print("===============================================")
     return json.dumps(file)
 
 
diff --git a/RegularExpressionParser/Parser.py b/RegularExpressionParser/Parser.py
index 8df3f22..abb8e02 100644
--- a/RegularExpressionParser/Parser.py
+++ b/RegularExpressionParser/Parser.py
@@ -77,6 +77,8 @@ def parse_query_to_json(pair):
                 else:
                     return {elements[0].split(".")[1]: {"$regex": elements[1], "$options": "i"}}
         else:
+            if len(elements[0].split(".")) != 2:
+                return {"wrong": "True"}
             if re.search(">", pair):
                 return {elements[0].split(".")[1]: {"$gt": float(elements[1])}}
             elif re.search("<", pair):
diff --git a/Server/SimpleServer.py b/Server/SimpleServer.py
index fa1ed3b..bdc8d5c 100644
--- a/Server/SimpleServer.py
+++ b/Server/SimpleServer.py
@@ -7,8 +7,10 @@ from flask import request
 from flask import abort
 from flask import jsonify
 from urllib.parse import urlparse, parse_qs
+from flask_cors import CORS
 
 app = Flask(__name__)
+cors = CORS(app)
 # app.config["DEBUG"] = True
 
 
@@ -26,22 +28,33 @@ def data_base(collection):
             url_parsed = urlparse(request.url)
             qs_parsed = parse_qs(url_parsed.query)
             if qs_parsed == {}:
-                return jsonify(DataBase.get_documents_json(0, {}))
-            return jsonify(search_document(["book.id:" + qs_parsed["id"][0]]))
+                return DataBase.get_documents_json(0, {})
+            result = search_document(["book.id:" + qs_parsed["id"][0]])
+            if result == {"wrong": "True"}:
+                abort(400, "Bad Request")
+            else:
+                return result
         elif collection == "author":
             url_parsed = urlparse(request.url)
             qs_parsed = parse_qs(url_parsed.query)
             if qs_parsed == {}:
-                return jsonify(DataBase.get_documents_json(1, {}))
-            return jsonify(search_document(["author.id:" + qs_parsed["id"][0]]))
+                return DataBase.get_documents_json(1, {})
+            result = search_document(["author.id:" + qs_parsed["id"][0]])
+            if result == {"wrong": "True"}:
+                abort(400, "Bad Request")
+            else:
+                return result
         elif collection == "search":
             url_parsed = urlparse(request.url)
             query = Parser.parse_url_to_query(url_parsed.query)
             qs_parsed = query.replace("q=", "")
             result = search_document(qs_parsed.split("&"))
-            return result
+            if result == {"wrong": "True"}:
+                abort(400, "Bad Request")
+            else:
+                return result
         else:
-            abort(404)
+            abort(400, "Bad Request")
     elif request.method == "PUT":
         if request.headers["Content-Type"] != "application/json":
             abort(415)
@@ -51,7 +64,7 @@ def data_base(collection):
         elif collection == "author":
             opt = 1
         else:
-            abort(400)
+            abort(400, "Bad Request")
         DataBase.update_dicts(opt, request.args.to_dict(), json_update_info)
         return "200: PUT succeeded"
     elif request.method == "POST":
@@ -74,7 +87,7 @@ def data_base(collection):
             Scrape.scrape_api(url, max_book, max_author)
             return "200: new data has been added to database"
         else:
-            abort(400)
+            abort(400, "Bad Request")
         return "200: POST succeeded"
     elif request.method == "DELETE":
         identifier = request.args.to_dict()
@@ -93,6 +106,8 @@ def search_document(identifiers):
     """ function used to find one or several document in database """
     if len(identifiers) == 1:
         json_idt = Parser.parse_query_to_json(identifiers[0])
+        if json_idt == {"wrong": "True"}:
+            return {"wrong": "True"}
         print(json_idt)
         if re.search("^book.*", identifiers[0]):
             return DataBase.get_documents_json(0, json_idt)
@@ -113,6 +128,8 @@ def search_document(identifiers):
                 opt = 1
         json_idt1 = Parser.parse_query_to_json(identifiers[0])
         json_idt2 = Parser.parse_query_to_json(identifiers[2])
+        if json_idt1 == {"wrong": "True"} or json_idt2 == {"wrong": "True"}:
+            return {"wrong": "True"}
         if identifiers[1] == "AND":
             exp = {"$and": [json_idt1, json_idt2]}
         elif identifiers[1] == "OR":
@@ -124,7 +141,8 @@ def search_document(identifiers):
         print(exp)
         return DataBase.get_documents_json(opt, exp)
     else:
-        return "Error, unknown identifiers"
+        print("Error, unknown identifiers")
+        return {}
 
 
 if __name__ == "__main__":
diff --git a/Tests/ServerTests.py b/Tests/ServerTests.py
index f069b7c..e0aff99 100644
--- a/Tests/ServerTests.py
+++ b/Tests/ServerTests.py
@@ -38,13 +38,13 @@ class DataBaseTests(unittest.TestCase):
         url = "http://127.0.0.1:5000/api/book?id=12345678"
         res = requests.get(url)
         self.assertEqual(res.status_code, 200)
-        self.assertEqual(res.json(), "{'books': []}")
+        self.assertEqual(res.json(), {'books': []})
 
     def test_invalid_get_wrong_collection_name(self):
         db.insert_document(self.test_data1, 0)
         url = "http://127.0.0.1:5000/api/bookssss?id=38746485"
         res = requests.get(url)
-        self.assertEqual(res.status_code, 404)
+        self.assertEqual(res.status_code, 400)
 
     def test_valid_put(self):
         db.insert_document(self.test_data1, 0)
diff --git a/web/app/src/App.js b/web/app/src/App.js
index de608eb..a219b6e 100644
--- a/web/app/src/App.js
+++ b/web/app/src/App.js
@@ -25,59 +25,139 @@
 //export default App;
 
 import React, { useState } from 'react';
-import FourButtons from './Components/FourButtons'
+import FourButtons from './Components/FourButtons';
+import { Dialog } from 'react-overlay-pack';
+import CorrectSign from './material/sign_correct.png';
+import ErrorSign from './material/sign_error.png';
+
 
 function App() {
     const { useState } = React;
     const [queryStr, setQueryStr] = useState('')
-    const [jsonValue, setJsonValue] = useState('')
+    const [sign, setSign] = useState(CorrectSign)
     const [text, setText] = useState('This area shows the result of requests..')
+    const [formState, setFormState] = useState('hide')
+    const [dialogState, setDialogState] = useState(false)
     const axios = require('axios').default;
+    var center = {display: 'flex', justifyContent: 'center', alignItems: 'center'}
+
+    function get() {
+        axios.get('http://127.0.0.1:5000/api/' + queryStr)
+        .then(function (response) {
+            // handle success
+            setSign(CorrectSign)
+            showDialog()
+            document.getElementById('dialog').value = response.status + ":\n" + response.statusText
+            document.getElementById('board').value = JSON.stringify(response.data)
+        })
+        .catch(function (error) {
+            // handle error
+            setSign(ErrorSign)
+            showDialog()
+            document.getElementById('dialog').value = error
+        })
+    }
+    function put() {
+        changeFormState()
+    }
     function mesg() {
-        alert('Hey!')
+        document.getElementById('board').value = 'Hello there!'
+    }
+    function changeFormState() {
+        if (formState === 'hide') {
+            setFormState('show')
+        } else {
+            setFormState('hide')
+        }
+    }
+    function showDialog() {
+        setDialogState(true)
     }
     return (
         <div>
+            {dialogState === true &&
+            <Dialog
+                show={dialogState}
+                onOutsideClick={() => setDialogState(false)}>
+                    <div style={{marginTop:'20%'}}>
+                        <div style={{
+                                    marginLeft: '35%'
+                                }}>
+                            <img src={sign}/>
+                        </div>
+                        <div>
+                            <textarea id='dialog' 
+                                readOnly={true}
+                                rows="3" 
+                                cols="38"
+                                style={{
+                                    border:'2px solid silver', 
+                                    backgroundColor: 'white', 
+                                    fontSize: '200%',
+                                    margin: '50px'
+                                }}>
+                                {text}
+                            </textarea>
+                        </div>
+                    </div>
+            </Dialog>}
             <form name='mainform'>
-                <h1> Welcome to the home page of GoodReads Crawler! </h1>
-                <h3> Please input your query string:</h3>
-                <input 
-                    id='queryString' 
-                    type='text' 
-                    placeholder='example: book?id=12345678' 
-                    size='40' 
-                    value={queryStr}
-                    onChange={(e) => setQueryStr(e.target.value)}
-                />
-                <h3> Please input your json parameters (only effective for POST and PUT):</h3>
-                <input 
-                    id='jsonValue' 
-                    type='text' 
-                    placeholder='example: {"rating_counr": 1000000}' 
-                    size='40' 
-                    value={jsonValue}
-                    onChange={(e) => setJsonValue(e.target.value)}
-                />
+                <h1 style={center}> 
+                    Welcome to the home page of GoodReads Crawler! </h1>
+                <h3 style={center}> Please input your query string:</h3>
+                <div style={center}>
+                    <input 
+                        id='queryString' 
+                        type='text' 
+                        placeholder='example: book?id=12345678' 
+                        size='40' 
+                        value={queryStr}
+                        onChange={(e) => setQueryStr(e.target.value)}
+                    />
+                </div>
+                <h3 style={center}> Please input your data for uploading (only effective for POST and PUT):</h3>
+                {formState === 'hide' && 
+                    <div style={center}>
+                        <button style={{width:60}} onClick={changeFormState}>
+                            book
+                        </button>
+                        <button style={{marginLeft:10, width:60}} onClick={changeFormState}>
+                            author
+                        </button>
+                    </div>}
+                {formState === 'show' &&
+                    <form style={center}>
+                        <table border="1" style="width:100%;">
+                            <tr>
+                                <td>Cell 1</td>
+                                <input/>
+                            </tr>
+                            <tr>
+                                <td>Cell 3</td>
+                                <input/>
+                            </tr>
+                        </table>
+                    </form>}
             </form>
-            <div style={{marginTop:20}}>
+            <div style={{marginTop:20, display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
                 <FourButtons 
                     backColor='blue' 
                     borderColor='RoyalBlue'
                     leftMargin={0}
                     text='GET'
-                    func={mesg} />
+                    func={get} />
                 <FourButtons 
                     backColor='black' 
                     borderColor='gray'
                     leftMargin={20}
                     text='PUT'
-                    func={mesg} />
+                    func={put} />
                 <FourButtons 
                     backColor='green' 
                     borderColor='seagreen'
                     leftMargin={20}
                     text='POST'
-                    func={mesg} />
+                    func={showDialog} />
                 <FourButtons 
                     backColor='red' 
                     borderColor='indianred'
@@ -85,7 +165,7 @@ function App() {
                     text='DELETE'
                     func={mesg} />
             </div>
-            <div style={{marginTop:20}}>
+            <div style={{marginTop:20, display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
                 <textarea id="board" name="returnData" rows="6" cols="50" value={text} readOnly={true}>
                 </textarea>
             </div>
diff --git a/web/app/src/material/sign_correct.png b/web/app/src/material/sign_correct.png
new file mode 100644
index 0000000000000000000000000000000000000000..f4c62915fc0b2c590da99790c4369a3e73f75cfe
GIT binary patch
literal 8706
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

literal 0
HcmV?d00001

diff --git a/web/app/src/material/sign_error.png b/web/app/src/material/sign_error.png
new file mode 100644
index 0000000000000000000000000000000000000000..52c8cb30694f970a86ed6baf14741e1a03183ca1
GIT binary patch
literal 9611
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
BL$Ck<

literal 0
HcmV?d00001

-- 
GitLab