diff --git a/README.md b/README.md
index df53d8a..1ac45c1 100644
--- a/README.md
+++ b/README.md
@@ -5,24 +5,26 @@
Welcome to Final Projects. This repository will serve as the starter for your final full-stack project.
Before starting, make sure:
-- [ ] You have chosen your teammates for the project
-- [ ] You have created an organisation and all your teammates are members
+- [x] You have chosen your teammates for the project
+- [x] You have created an organisation and all your teammates are members
Make sure that you have already created your own **organisation** with your teammates
## Get Started
-- [ ] Fork the repository under your organisation
-- [ ] Each member should clone the forked repository on his local machine
-- [ ] Choose one of the projects that has not been reserved
+- [x] Fork the repository under your organisation
+- [x] Each member should clone the forked repository on his local machine
+- [x] Choose one of the projects that has not been reserved
## Team composition
Edit this file by filling your team composition here:
-- [ ] _Student full name_ | _Github username_ | **Scrum Master**
-- [ ] _Student full name_ | _Gihub username_
-- [ ] _Student full name_ | _Gihub username_
+- [x] Mahmoud Mansouri | MahmoudMans | **Scrum Master**
+- [x] Mohamed Achraf Liratni | MohamedLiratni
+- [x] Nour Safta | Nour
+
+For our project , we ensured that all of the team members has contributed equally to build a su full-stack application. So, we divided the work as follows: -Part 1 and 2 :each one of us did it alone then we communicated with each other through online and face-to-face meetings to track our progress,and finally each member put his work in his own branch. -Starting from Part 3 till Part 6, we did it all together on the same laptop through face-to-face meetings.
## Technologies
@@ -34,7 +36,7 @@ In you assignment, you have to use all the following Technologies
Choose one of these Technologies for your DBMS and your ORM
-- [ ] MongoDB + Mongoose
+- [x] MongoDB + Mongoose
- [ ] MySQL + Sequelize
Feel free to use any of these technologies
@@ -51,6 +53,7 @@ Your final Deadline for submission is **`the 11th of December 2021`**. A weekly
To submit your work:
-- [ ] Make a demo of your project and upload it to [this folder](https://drive.google.com/drive/folders/14ndlnd1BK9EF7XdZLrgrNdtidr3X-r0a?usp=sharing). If you feel like you need help to get over this, refer to the steps in the [recording guidelines](./RECORDING.md).
-- [ ] Deploy your application to one of these [free hosting services](https://blogs.devchallenges.io/posts/tJ26U8MhZTPgBSRSwpqr) or to [heroku](https://www.heroku.com/)
-- [ ] Create a Pull Request from your forked repository
+- [x] Make a demo of your project and upload it to [this folder](https://drive.google.com/drive/folders/14ndlnd1BK9EF7XdZLrgrNdtidr3X-r0a?usp=sharing). If you feel like you need help to get over this, refer to the steps in the [recording guidelines](./RECORDING.md).
+- [ ] Deploy your application to one of these [free hosting services](https://blogs.devchallenges.io/posts/tJ26U8MhZTPgBSRSwpqr) or to [heroku](https://www.heroku.com/)(we got problem when deploying to the heroku website)
+
+- [x] Create a Pull Request from your forked repository
diff --git a/hacker-news/2021-12-31 16-07-36.mkv b/hacker-news/2021-12-31 16-07-36.mkv
new file mode 100644
index 0000000..9221ac4
Binary files /dev/null and b/hacker-news/2021-12-31 16-07-36.mkv differ
diff --git a/hacker-news/client/.gitignore b/hacker-news/client/.gitignore
new file mode 100644
index 0000000..4d29575
--- /dev/null
+++ b/hacker-news/client/.gitignore
@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/hacker-news/client/README.md b/hacker-news/client/README.md
new file mode 100644
index 0000000..58beeac
--- /dev/null
+++ b/hacker-news/client/README.md
@@ -0,0 +1,70 @@
+# Getting Started with Create React App
+
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.\
+Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
+
+The page will reload when you make changes.\
+You may also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.\
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.\
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.\
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can't go back!**
+
+If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
+
+You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
+
+### Code Splitting
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
+
+### Analyzing the Bundle Size
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
+
+### Making a Progressive Web App
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
+
+### Advanced Configuration
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
+
+### Deployment
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
+
+### `npm run build` fails to minify
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
diff --git a/hacker-news/client/package.json b/hacker-news/client/package.json
new file mode 100644
index 0000000..b42f0c1
--- /dev/null
+++ b/hacker-news/client/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "client",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@testing-library/jest-dom": "^5.16.1",
+ "@testing-library/react": "^12.1.2",
+ "@testing-library/user-event": "^13.5.0",
+ "axios": "^0.24.0",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "react-router-dom": "^6.2.1",
+ "react-scripts": "5.0.0",
+ "web-vitals": "^2.1.2"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/hacker-news/client/public/favicon.ico b/hacker-news/client/public/favicon.ico
new file mode 100644
index 0000000..a11777c
Binary files /dev/null and b/hacker-news/client/public/favicon.ico differ
diff --git a/hacker-news/client/public/index.html b/hacker-news/client/public/index.html
new file mode 100644
index 0000000..aa069f2
--- /dev/null
+++ b/hacker-news/client/public/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ React App
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
diff --git a/hacker-news/client/public/logo192.png b/hacker-news/client/public/logo192.png
new file mode 100644
index 0000000..fc44b0a
Binary files /dev/null and b/hacker-news/client/public/logo192.png differ
diff --git a/hacker-news/client/public/logo512.png b/hacker-news/client/public/logo512.png
new file mode 100644
index 0000000..a4e47a6
Binary files /dev/null and b/hacker-news/client/public/logo512.png differ
diff --git a/hacker-news/client/public/manifest.json b/hacker-news/client/public/manifest.json
new file mode 100644
index 0000000..080d6c7
--- /dev/null
+++ b/hacker-news/client/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "React App",
+ "name": "Create React App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/hacker-news/client/public/robots.txt b/hacker-news/client/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/hacker-news/client/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/hacker-news/client/src/App.css b/hacker-news/client/src/App.css
new file mode 100644
index 0000000..e8702f0
--- /dev/null
+++ b/hacker-news/client/src/App.css
@@ -0,0 +1,51 @@
+.App {
+ text-align: center;
+}
+
+.aParent {
+ float: left;
+ width: 10%;
+}
+.nowrap {
+ white-space: nowrap;
+}
+
+.title {
+ background-color: lightgreen;
+}
+
+.author {
+ background-color: lightgreen;
+}
+
+.authors {
+ float: left;
+}
+nav {
+ height: 50px;
+ background-color: greenyellow;
+}
+.liste {
+ list-style-type: none;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+}
+
+.items {
+ margin-right: 10px;
+ color: black;
+ cursor: pointer;
+}
+
+span {
+ display: inline-block;
+ vertical-align: middle;
+ line-height: normal;
+}
+
+.link {
+ text-decoration: none;
+}
diff --git a/hacker-news/client/src/App.js b/hacker-news/client/src/App.js
new file mode 100644
index 0000000..f972749
--- /dev/null
+++ b/hacker-news/client/src/App.js
@@ -0,0 +1,45 @@
+import logo from "./logo.svg";
+import "./App.css";
+import Stories from "./components/Stories";
+import Authors from "./components/Authors";
+import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
+import Search from "./components/Search";
+import Test from "./components/Test";
+import Recommended from "./components/Recommended";
+
+function App() {
+ setTimeout(function () {
+ window.location.reload();
+ console.log("it refresh");
+ }, 60 * 1000);
+ return (
+
+
+
+
+
+ TOP 10 Stories
+
+
+ TOP 10 Authors
+
+
+ Seach by Author
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+ {/*
*/}
+ {/*
*/}
+
+ );
+}
+
+export default App;
diff --git a/hacker-news/client/src/App.test.js b/hacker-news/client/src/App.test.js
new file mode 100644
index 0000000..1f03afe
--- /dev/null
+++ b/hacker-news/client/src/App.test.js
@@ -0,0 +1,8 @@
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+ render( );
+ const linkElement = screen.getByText(/learn react/i);
+ expect(linkElement).toBeInTheDocument();
+});
diff --git a/hacker-news/client/src/components/Authors.js b/hacker-news/client/src/components/Authors.js
new file mode 100644
index 0000000..2cba391
--- /dev/null
+++ b/hacker-news/client/src/components/Authors.js
@@ -0,0 +1,52 @@
+import React, { useState, useEffect } from "react";
+import Axios from "axios";
+
+export default function Authors() {
+ const [listOfAuthors, setListOfAuthors] = useState([]);
+
+ useEffect(() => {
+ Axios.get("http://localhost:8000/api/author").then((response) => {
+ setListOfAuthors(response.data);
+ });
+ }, []);
+ return (
+
+
+
Hacked News
+
Top 10 Authors
+
+
+
name
+ {listOfAuthors.map((user) => {
+ return (
+
+
name: {user._id}
+ {/*
karma :{user.karma}
+
about : {user.about}
*/}
+
+ );
+ })}
+
+
+
karma
+ {listOfAuthors.map((user) => {
+ return
{user.karma}
;
+ })}
+
+
+
about
+ {listOfAuthors.map((user) => {
+ return user.about ? (
+
{user.about}
+ ) : (
+
+ no data available in the api about this section
+
+ );
+ })}
+
+
+
+
+ );
+}
diff --git a/hacker-news/client/src/components/Recommended.js b/hacker-news/client/src/components/Recommended.js
new file mode 100644
index 0000000..bceb3ad
--- /dev/null
+++ b/hacker-news/client/src/components/Recommended.js
@@ -0,0 +1,58 @@
+import React, { useState, useEffect } from "react";
+import Axios from "axios";
+import { useNavigate, useParams, useResolvedPath } from "react-router-dom";
+
+export default function Recommended() {
+ var ids = [];
+ var arr = []; // Array to contain match elements
+ const [listOfUsers, setListOfUsers] = useState([]);
+ let { stories } = useParams();
+ console.log(stories);
+ useEffect(() => {
+ Axios.get("http://localhost:8000/api/stories").then((response) => {
+ setListOfUsers(response.data);
+ });
+ }, []);
+ return (
+
+
similar story to "{JSON.stringify(stories)}"
+
+
Title
+
+ {listOfUsers.map((user) => {
+ if (user.title == stories) {
+ ids.push(user.kids);
+ // console.log(user.kids);
+ }
+ })}
+ {listOfUsers.forEach((stori) => {
+ function arrayMatch(arr1, arr2) {
+ for (var i = 0; i < arr1.length; ++i) {
+ for (var j = 0; j < arr2.length; ++j) {
+ if (arr1[i] == arr2[j]) {
+ // If element is in both the arrays
+ arr.push(arr1[i]); // Push to arr array
+ }
+ }
+ }
+ if (arr.length == 0) {
+ return
There is no similar stories in the top 10 stories ;
+ }
+ return arr; // Return the arr elements
+ }
+ console.log(arrayMatch(stori.kids, ids));
+ })}
+ {listOfUsers.map((user) => {
+ return arr.length == 0 ? (
+
No Similar stories
+ ) : (
+
+ {" "}
+ {user.title}
{user.by}
+
+ );
+ })}
+
+
+ );
+}
diff --git a/hacker-news/client/src/components/Search.js b/hacker-news/client/src/components/Search.js
new file mode 100644
index 0000000..3dad3bc
--- /dev/null
+++ b/hacker-news/client/src/components/Search.js
@@ -0,0 +1,75 @@
+import React, { useState, useEffect } from "react";
+import Axios from "axios";
+
+export default function Search() {
+ const [listOfAuthors, setListOfAuthors] = useState([]);
+ const [listOfStory, setListOfStory] = useState([]);
+ const [searchTerm, setSearchTerm] = useState("");
+ // useEffect(() => {
+ // Axios.get("http://localhost:8000/api/author").then((response) => {
+ // setListOfStory(response.data);
+ // });
+ // }, []);
+
+ useEffect(() => {
+ Axios.get("http://localhost:8000/api/stori").then((response) => {
+ setListOfAuthors(response.data);
+ });
+ }, []);
+
+ // const searchAuthor =() =>{
+ // axios.post("").then((response)=>{
+ // setListOfAuthors(response)
+ // })
+ // }
+
+ return (
+
+
{
+ setSearchTerm(event.target.value);
+ }}
+ />
+
Search
+
+ {/* {listOfAuthors.map((val, key) => {
+ return (
+
+ );
+ })} */}
+ {listOfAuthors
+ .filter((user) => {
+ if (searchTerm == "") {
+ // return user;
+ } else if (
+ user.by.toLowerCase().includes(searchTerm.toLocaleLowerCase())
+ ) {
+ return user;
+ }
+ })
+ .map((user, key) => {
+ return (
+
+
author: {user.by}
+
title: {user.title}
+
comments: {user.kids.length}
+
Points: {user.score}
+ {/*
{user.score}
*/}
+ {/*
+ {listOfStory.map((val, key) => {
+
{val.by} ;
+
{val.title}
+ //
{val.by}
;
+ })} */}
+ {/*
karma :{user.karma}
+
about : {user.about}
*/}
+
+ );
+ })}
+
+ );
+}
diff --git a/hacker-news/client/src/components/Stories.js b/hacker-news/client/src/components/Stories.js
new file mode 100644
index 0000000..4375dd2
--- /dev/null
+++ b/hacker-news/client/src/components/Stories.js
@@ -0,0 +1,40 @@
+import React, { useState, useEffect } from "react";
+import Axios from "axios";
+import { Link } from "react-router-dom";
+
+export default function Stories() {
+ const [listOfUsers, setListOfUsers] = useState([]);
+
+ useEffect(() => {
+ Axios.get("http://localhost:8000/api/stories").then((response) => {
+ setListOfUsers(response.data);
+ });
+ }, []);
+
+ return (
+
+
Hacked News
+
Top 10 Stories
+
+
+
Title
+ {listOfUsers.map((user) => {
+ return (
+
+ {" "}
+
+
{user.title}
+
+
+ );
+ })}
+
+
+
Author
+ {listOfUsers.map((user) => {
+ return
{user.by}
;
+ })}
+
+
+ );
+}
diff --git a/hacker-news/client/src/components/Test.js b/hacker-news/client/src/components/Test.js
new file mode 100644
index 0000000..c65f3d3
--- /dev/null
+++ b/hacker-news/client/src/components/Test.js
@@ -0,0 +1,24 @@
+import React, { useState, useEffect } from "react";
+import Axios from "axios";
+
+export default function Test() {
+ const [listOfAuthors, setListOfAuthors] = useState([]);
+
+ useEffect(() => {
+ Axios.get("http://localhost:8000/api/stori").then((response) => {
+ setListOfAuthors(response.data);
+ });
+ }, []);
+ return (
+
+
hi
+ {listOfAuthors.map((val) => {
+ return (
+
+
{val.id}
+
+ );
+ })}
+
+ );
+}
diff --git a/hacker-news/client/src/index.css b/hacker-news/client/src/index.css
new file mode 100644
index 0000000..ec2585e
--- /dev/null
+++ b/hacker-news/client/src/index.css
@@ -0,0 +1,13 @@
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
diff --git a/hacker-news/client/src/index.js b/hacker-news/client/src/index.js
new file mode 100644
index 0000000..ef2edf8
--- /dev/null
+++ b/hacker-news/client/src/index.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('root')
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();
diff --git a/hacker-news/client/src/logo.svg b/hacker-news/client/src/logo.svg
new file mode 100644
index 0000000..9dfc1c0
--- /dev/null
+++ b/hacker-news/client/src/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/hacker-news/client/src/reportWebVitals.js b/hacker-news/client/src/reportWebVitals.js
new file mode 100644
index 0000000..5253d3a
--- /dev/null
+++ b/hacker-news/client/src/reportWebVitals.js
@@ -0,0 +1,13 @@
+const reportWebVitals = onPerfEntry => {
+ if (onPerfEntry && onPerfEntry instanceof Function) {
+ import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+ getCLS(onPerfEntry);
+ getFID(onPerfEntry);
+ getFCP(onPerfEntry);
+ getLCP(onPerfEntry);
+ getTTFB(onPerfEntry);
+ });
+ }
+};
+
+export default reportWebVitals;
diff --git a/hacker-news/client/src/setupTests.js b/hacker-news/client/src/setupTests.js
new file mode 100644
index 0000000..8f2609b
--- /dev/null
+++ b/hacker-news/client/src/setupTests.js
@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';
diff --git a/hacker-news/dummy_data.js b/hacker-news/dummy_data.js
index 81bd5a4..5ade63b 100644
--- a/hacker-news/dummy_data.js
+++ b/hacker-news/dummy_data.js
@@ -1,22 +1,22 @@
module.exports = [
- {
- 'by': {
- 'about': 'My name is Erik',
- 'id': 'erikdbrown',
- 'karma': 15319,
- },
- 'id': 123456789,
- 'score': 392,
- 'title': 'How to write fun technical assessments for everyone!',
+ {
+ by: {
+ about: "My name is Erik",
+ id: "erikdbrown",
+ karma: 15319,
},
- {
- 'by': {
- 'about': 'my favorite color is grey',
- 'id': 'beth',
- 'karma': 1000,
- },
- 'id': 10000000,
- 'score': 497,
- 'title': 'Where to find all of the cool dogs',
- }
- ];
\ No newline at end of file
+ id: 123456789,
+ score: 392,
+ title: "How to write fun technical assessments for everyone!",
+ },
+ {
+ by: {
+ about: "my favorite color is grey",
+ id: "beth",
+ karma: 1000,
+ },
+ id: 10000000,
+ score: 497,
+ title: "Where to find all of the cool dogs",
+ },
+];
diff --git a/hacker-news/server/db/index.js b/hacker-news/server/db/index.js
index 433babf..cee5f58 100644
--- a/hacker-news/server/db/index.js
+++ b/hacker-news/server/db/index.js
@@ -1,3 +1,22 @@
-/**
- * Your Database Connection comes here
- */
\ No newline at end of file
+const mongoose = require("mongoose");
+
+const connectiondb = () => {
+ mongoose
+ .connect(
+ "mongodb+srv://Mahmoud:Midou2001@cluster0.h0gxs.mongodb.net/hackernewsdb?retryWrites=true&w=majority",
+ {
+ useNewUrlParser: true,
+ useUnifiedTopology: true,
+ }
+ )
+ .then(() => {
+ console.log("Connected to MongoDB...");
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+};
+module.exports = connectiondb;
+
+//mongodb://localhost:27017/hackernewsdb
+//mongodb+srv://Mahmoud:@cluster0.h0gxs.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
diff --git a/hacker-news/server/index.js b/hacker-news/server/index.js
index 21a0968..846d3ad 100644
--- a/hacker-news/server/index.js
+++ b/hacker-news/server/index.js
@@ -1,3 +1,22 @@
-/**
- * Your server comes here
-**/
\ No newline at end of file
+const express = require("express");
+const dbconnection = require("./db/index");
+const Story = require("./models/Story");
+const storiesroute = require("./routes/stories.routes");
+const authorsroute = require("./routes/authors.routes");
+const app = express();
+
+const cors = require("cors");
+const Author = require("./models/Author");
+const PORT = process.env.PORT || 8000;
+app.use(express.json());
+app.use(cors());
+dbconnection();
+
+app.use("/", storiesroute);
+app.use("/", authorsroute);
+
+if (process.env.NODE_ENV === "production") {
+ app.use(express.static("../client/build"));
+}
+
+app.listen(PORT, () => console.log(`server listening on port ${PORT}!`));
diff --git a/hacker-news/server/models/Author.js b/hacker-news/server/models/Author.js
new file mode 100644
index 0000000..3a77009
--- /dev/null
+++ b/hacker-news/server/models/Author.js
@@ -0,0 +1,19 @@
+const mongoose = require("mongoose");
+
+const authorSchema = new mongoose.Schema({
+ id: {
+ type: String,
+ required: true,
+ },
+ about: {
+ type: String,
+ },
+ karma: {
+ type: Number,
+ required: true,
+ },
+});
+
+const Author = mongoose.model("Author", authorSchema);
+
+module.exports = Author;
diff --git a/hacker-news/server/models/Story.js b/hacker-news/server/models/Story.js
new file mode 100644
index 0000000..3e2e54f
--- /dev/null
+++ b/hacker-news/server/models/Story.js
@@ -0,0 +1,27 @@
+const mongoose = require("mongoose");
+
+const storySchema = new mongoose.Schema({
+ id: {
+ type: Number,
+ required: true,
+ },
+ by: {
+ type: Object,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ score: {
+ type: Number,
+ required: true,
+ },
+ kids: {
+ type: Array,
+ },
+});
+
+const Story = mongoose.model("Story", storySchema);
+
+module.exports = Story;
diff --git a/hacker-news/server/package.json b/hacker-news/server/package.json
new file mode 100644
index 0000000..eff42f7
--- /dev/null
+++ b/hacker-news/server/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "server",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "cd ../client && npm run build",
+ "install-client": "cd ../client && npm install",
+ "keroku-postbuild" :"npm run install-client && npm run build",
+ "start": "nodemon index.js",
+ "seed-database": "node seed.js",
+ "newseed-database": "node seedwork.js"
+
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "body-parser": "^1.19.1",
+ "cors": "^2.8.5",
+ "express": "^4.17.2",
+ "mongoose": "^6.1.3",
+ "nodemon": "^2.0.15",
+ "request": "^2.88.2"
+ }
+}
diff --git a/hacker-news/server/routes/authors.routes.js b/hacker-news/server/routes/authors.routes.js
new file mode 100644
index 0000000..4009608
--- /dev/null
+++ b/hacker-news/server/routes/authors.routes.js
@@ -0,0 +1,58 @@
+const router = require("express").Router();
+const Story = require("../models/Story");
+const Author = require("../models/Author");
+
+router.get("/api/author", (req, res) => {
+ // Author.find()
+ // .sort({ karma: -1 })
+
+ // .distinct("id")
+ // .exec(function (err, result) {
+ // if (err) {
+ // console.log(err);
+ // } else {
+ // res.json(result);
+ // }
+ // });
+ // Author.distinct("id").exec(function (err, result) {
+ // if (err) {
+ // console.log(err);
+ // } else {
+ // res.json(result);
+ // }
+ // });
+ Author.aggregate([
+ {
+ $group: {
+ _id: { id: "$id", karma: "$karma", about: "$about" },
+ },
+ },
+ {
+ $group: {
+ _id: "$_id.id",
+ karma: { $first: "$_id.karma" },
+ about: { $first: "$_id.about" },
+ count: { $sum: 1 },
+ },
+ },
+ ]).exec(function (err, results) {
+ if (err) {
+ console.log(err);
+ } else {
+ res.json(results);
+ }
+ });
+});
+
+// router.post("/search", async (req, res) => {
+// const author = req.body;
+// Author.find({ id: "andrewnc" }, (err, result) => {
+// if (err) {
+// console.log(err);
+// } else {
+// res.json(result);
+// }
+// });
+// });
+
+module.exports = router;
diff --git a/hacker-news/server/routes/stories.routes.js b/hacker-news/server/routes/stories.routes.js
new file mode 100644
index 0000000..9e34d2b
--- /dev/null
+++ b/hacker-news/server/routes/stories.routes.js
@@ -0,0 +1,68 @@
+const router = require("express").Router();
+const Story = require("../models/Story");
+
+router.get("/api/stories/", (req, res) => {
+ Story.find({}, (err, result) => {
+ if (err) {
+ console.log(err);
+ } else {
+ res.json(result);
+ }
+ });
+});
+
+router.get("/api/stori", (req, res) => {
+ // Story.aggregate([
+ // {
+ // $group: {
+ // _id: { kids: "$kids" },
+
+ // count: { $sum: "$kids" },
+ // },
+ // },
+ // ]).exec(function (err, results) {
+ // if (err) {
+ // console.log(err);
+ // } else {
+ // res.json(results);
+ // }
+ // });
+ // Story.aggregate([
+ // {
+ // $project: {
+ // item: 1,
+ // numberofcom: {
+ // $cond: {
+ // if: { $isArray: "$kids" },
+ // then: { $size: "$kids" },
+ // else: "NA",
+ // },
+ // },
+ // },
+ // },
+ // ]).exec(function (err, results) {
+ // if (err) {
+ // console.log(err);
+ // } else {
+ // res.json(results);
+ // }
+ // });
+ Story.aggregate([
+ {
+ $lookup: {
+ from: "authors",
+ localField: "by",
+ foreignField: "id",
+ as: "authstory",
+ },
+ },
+ ]).exec(function (err, results) {
+ if (err) {
+ console.log(err);
+ } else {
+ res.json(results);
+ }
+ });
+});
+
+module.exports = router;
diff --git a/hacker-news/server/seed.js b/hacker-news/server/seed.js
new file mode 100644
index 0000000..0621902
--- /dev/null
+++ b/hacker-news/server/seed.js
@@ -0,0 +1,24 @@
+const mongoose = require("mongoose");
+const Story = require("./models/Story");
+const seedStory = require("../dummy_data");
+const dbconnection = require("./db/index");
+const Author = require("./models/Author");
+
+dbconnection();
+
+var x = [];
+for (const obj of seedStory) {
+ x.push(obj.by);
+}
+const seedDB = async () => {
+ await Story.deleteMany({});
+ await Story.insertMany(seedStory);
+
+ await Author.deleteMany({});
+ await Author.insertMany(x);
+};
+
+console.log(x);
+seedDB().then(() => {
+ mongoose.connection.close();
+});
diff --git a/hacker-news/server/seedwork.js b/hacker-news/server/seedwork.js
new file mode 100644
index 0000000..f9dfff0
--- /dev/null
+++ b/hacker-news/server/seedwork.js
@@ -0,0 +1,253 @@
+const mongoose = require("mongoose");
+const request = require("request");
+const Story = require("./models/Story");
+const Author = require("./models/Author");
+mongoose.connect(
+ "mongodb+srv://Mahmoud:Midou2001@cluster0.h0gxs.mongodb.net/hackernewsdb?retryWrites=true&w=majority"
+);
+
+Author.deleteMany({}).then(() => {
+ console.log("success");
+});
+Story.deleteMany({}).then(() => {
+ console.log("success");
+});
+
+var topStoriesURL = "https://hacker-news.firebaseio.com/v0/topstories.json/";
+var data = null;
+var datas = null;
+var datass = null;
+var datasss = null;
+var i = 0;
+var j = 0;
+var k = 0;
+var authors = [];
+
+var idcomment = [];
+var idcomments = [];
+//var topStoriesinfo = `https://hacker-news.firebaseio.com/v0/item/${id[i]}.json`;
+var headerres = (headers) => {
+ return headers["content-type"].includes("json");
+};
+
+var topStories = function (url, callback) {
+ request.get(url, function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ data = JSON.parse(body);
+ callback(null, data);
+ }
+ });
+};
+
+var storiesinfo = (callback) => {
+ for (i = 0; i < 10; i++)
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/item/${data[i]}.json`,
+ function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ datas = JSON.parse(body);
+ authors.push(datas.by);
+ idcomment.push(datas.kids);
+ callback(null, datas);
+ }
+ }
+ );
+};
+
+var authinfo = (callback) => {
+ for (j = 0; j < 10; j++) {
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[j]}.json`,
+ function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ datass = JSON.parse(body);
+
+ callback(null, datass);
+ }
+ }
+ );
+ }
+};
+
+var commentinfo = (callback) => {
+ for (k = 0; k < idcomments.length; k++) {
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/item/${idcomments[k]}.json`,
+ function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ datasss = JSON.parse(body);
+
+ callback(null, datasss);
+ }
+ }
+ );
+ }
+};
+
+topStories(topStoriesURL, (err, data) => {
+ // console.log(data);
+});
+
+setTimeout(() => {
+ storiesinfo((err, datas) => {
+ // console.log(datas);
+ Story.insertMany(datas);
+ // console.log(authors);
+ // console.log(idcomment);
+ idcomments = [].concat(...idcomment);
+ // console.log(idcomments);
+ });
+}, 2000);
+
+setTimeout(() => {
+ authinfo((err, datass) => {
+ Author.insertMany(datass);
+ // console.log(datass);
+ });
+}, 3000);
+
+setTimeout(() => {
+ commentinfo((err, datass) => {
+ // console.log(datasss);
+ });
+}, 4000);
+
+setInterval(() => {
+ Author.deleteMany({}).then(() => {
+ console.log("success");
+ });
+ Story.deleteMany({}).then(() => {
+ console.log("success");
+ });
+
+ var topStoriesURL = "https://hacker-news.firebaseio.com/v0/topstories.json/";
+ var data = null;
+ var datas = null;
+ var datass = null;
+ var datasss = null;
+ var i = 0;
+ var j = 0;
+ var k = 0;
+ var authors = [];
+
+ var idcomment = [];
+ var idcomments = [];
+ //var topStoriesinfo = `https://hacker-news.firebaseio.com/v0/item/${id[i]}.json`;
+ var headerres = (headers) => {
+ return headers["content-type"].includes("json");
+ };
+
+ var topStories = function (url, callback) {
+ request.get(url, function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ data = JSON.parse(body);
+ callback(null, data);
+ }
+ });
+ };
+
+ var storiesinfo = (callback) => {
+ for (i = 0; i < 10; i++)
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/item/${data[i]}.json`,
+ function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ datas = JSON.parse(body);
+ authors.push(datas.by);
+ idcomment.push(datas.kids);
+ callback(null, datas);
+ }
+ }
+ );
+ };
+
+ var authinfo = (callback) => {
+ for (j = 0; j < 10; j++) {
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[j]}.json`,
+ function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ datass = JSON.parse(body);
+
+ callback(null, datass);
+ }
+ }
+ );
+ }
+ };
+
+ var commentinfo = (callback) => {
+ for (k = 0; k < idcomments.length; k++) {
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/item/${idcomments[k]}.json`,
+ function (err, response, body) {
+ if (err) {
+ callback(err, null);
+ } else if (!headerres(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ datasss = JSON.parse(body);
+
+ callback(null, datasss);
+ }
+ }
+ );
+ }
+ };
+
+ topStories(topStoriesURL, (err, data) => {
+ // console.log(data);
+ });
+
+ setTimeout(() => {
+ storiesinfo((err, datas) => {
+ // console.log(datas);
+ Story.insertMany(datas);
+ // console.log(authors);
+ // console.log(idcomment);
+ idcomments = [].concat(...idcomment);
+ // console.log(idcomments);
+ });
+ }, 2000);
+
+ setTimeout(() => {
+ authinfo((err, datass) => {
+ Author.insertMany(datass);
+ // console.log(datass);
+ });
+ }, 3000);
+
+ setTimeout(() => {
+ commentinfo((err, datass) => {
+ // console.log(datasss);
+ });
+ }, 4000);
+}, 60 * 1000);
diff --git a/hacker-news/server/worker.js b/hacker-news/server/worker.js
new file mode 100644
index 0000000..f9f3bb9
--- /dev/null
+++ b/hacker-news/server/worker.js
@@ -0,0 +1,156 @@
+/*
+the file that i used to seed the db is seedwork
+*/
+var mongoose = require("mongoose");
+var request = require("request");
+const Story = require("./models/Story");
+const dbconnection = require("./db/index");
+const Author = require("./models/Author");
+//dbconnection();
+mongoose.connect(
+ "mongodb+srv://Mahmoud:Midou2001@cluster0.h0gxs.mongodb.net/hackernewsdb?retryWrites=true&w=majority"
+);
+// In this file, build out a worker that will populate the database
+// with the data you need from the HackerNews API
+
+// Here is an example of getting the top 500 stories from the API
+// and logging them to the console.
+// You are not required to use this code (though you may).
+//var topStoriesURL = "https://hacker-news.firebaseio.com/v0/beststories.json";
+Author.deleteMany({}).then(() => {
+ console.log("success");
+});
+Story.deleteMany({}).then(() => {
+ console.log("success");
+});
+var topStoriesURL = "https://hacker-news.firebaseio.com/v0/topstories.json/";
+var data = null;
+var datas = null;
+var authdatas = null;
+var j;
+var i;
+var authors = [];
+var autho = [];
+var isJSONResponse = function (headers) {
+ return headers["content-type"].includes("json");
+};
+var arr = [];
+var id = [];
+var getJSONFromHackerNews = function (url, callback) {
+ request.get(url, function (err, response, body) {
+ var aut = null;
+ if (err) {
+ callback(err, null);
+ } else if (!isJSONResponse(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ data = JSON.parse(body);
+ for (i = 0; i < 10; i++) {
+ id.push(data[i]);
+ }
+
+ console.log(id);
+ callback(null, data);
+
+ for (j = 0; j < 10; j++) {
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/item/${id[j]}.json`,
+ async (err, response, body) => {
+ if (err) {
+ callback(err, null);
+ } else if (!isJSONResponse(response.headers)) {
+ callback(new Error("Response did not contain JSON data."), null);
+ } else {
+ datas = JSON.parse(body);
+
+ Story.insertMany(datas);
+
+ authors.push(datas.by);
+
+ console.log(authors);
+
+ await Author.deleteMany({}).then(() => {
+ console.log("success");
+ });
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[0]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[1]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[2]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[3]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[4]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[5]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[6]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[7]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[8]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ request.get(
+ `https://hacker-news.firebaseio.com/v0/user/${authors[9]}.json`,
+ async (err, response, body) => {
+ authdatas = JSON.parse(body);
+ await Author.insertMany(authdatas);
+ }
+ );
+ }
+ }
+ );
+ }
+ }
+ });
+};
+
+getJSONFromHackerNews(topStoriesURL, function (err, data, datas) {
+ console.log(err, "err, expect to be null");
+ console.log(data, "data, expect to be ids for top 500 stories");
+});
diff --git a/hacker-news/worker.js b/hacker-news/worker.js
deleted file mode 100644
index f784903..0000000
--- a/hacker-news/worker.js
+++ /dev/null
@@ -1,36 +0,0 @@
-var mongoose = require('mongoose');
-var request = require('request');
-
-mongoose.connect('mongodb://localhost/hackednews');
-// In this file, build out a worker that will populate the database
-// with the data you need from the HackerNews API
-
-
-// Here is an example of getting the top 500 stories from the API
-// and logging them to the console.
-// You are not required to use this code (though you may).
-var topStoriesURL = 'https://hacker-news.firebaseio.com/v0/topstories.json';
-
-var isJSONResponse = function(headers) {
- return headers['content-type'].includes('json');
-};
-
-var getJSONFromHackerNews = function (url, callback) {
- request.get(url, function(err, response, body) {
- var data = null;
- if (err) {
- callback(err, null);
- } else if (!isJSONResponse(response.headers)) {
- callback(new Error('Response did not contain JSON data.'), null);
- } else {
- data = JSON.parse(body);
- callback(null, data);
- }
- });
-};
-
-getJSONFromHackerNews(topStoriesURL, function(err, data) {
- console.log(err, 'err, expect to be null');
- console.log(data, 'data, expect to be ids for top 500 stories');
- mongoose.disconnect();
-});