├── .github └── ISSUE_TEMPLATE │ └── 북스터디-이슈-템플릿.md ├── .gitignore ├── README.md └── pickme ├── .idea ├── misc.xml ├── modules.xml ├── pickme.iml ├── vcs.xml └── workspace.xml ├── KangJiJi ├── .babelrc ├── .eslintignore.js ├── .eslintrc.js ├── .postcssrc ├── index.css ├── index.html ├── index.js ├── package-lock.json ├── package.json └── src │ ├── app.js │ ├── components │ ├── increaseParticipantButtonComponent.js │ ├── pickPresenterButtonComponent.js │ ├── setInformationOfParticipantComponent.js │ └── showPresenterComponent.js │ ├── helper │ ├── domHelper.js │ └── presenterPickHelper.js │ └── pages │ └── mainPage.js ├── hyesun03 ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── codeStyleConfig.xml │ ├── hyesun03.iml │ ├── misc.xml │ ├── modules.xml │ └── vcs.xml ├── crawling.py ├── crawling_test.json ├── main.py ├── requirements.txt ├── study_member.json └── test.py └── jisoo ├── index.html └── spec.md /.github/ISSUE_TEMPLATE/북스터디-이슈-템플릿.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 북스터디 이슈 템플릿 3 | about: 다음 스터디의 범위를 제목에 입력해주세요~ 4 | title: "[책 제목] 23.00.00/0주차 - 챕터 0. 챕터 제목까지 ~ 000p" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 스터디는 마이크를 끄지 않아요. 11 | 12 | 8시까지 해당 이슈에 책을 읽으면서 궁금했던 부분, 함께 이야기해보면 좋을것 같은 부분을 코멘트로 달아주세요. 13 | 아무것도 안올리면 그 다음회차 호스트하기 14 | 15 | 호스트는 우선적으로 스터디원들이 올린 코멘트에 대해 자신의 생각을 이야기하고, 다른 사람들은 어떻게 생각하는지 토론을 이끌어 나가는 역할을 수행합니다. 16 | 17 | . 18 | 우리 스터디에 멍청한 질문은 없습니다. 19 | 내가 이해 안 간 부분이 있다면 그건 높은 확률로 다른 사람도 이해를 못했을 것입니다. 20 | 21 | 우리 스터디는 이걸 위해서 진행합니다. 혼자서 읽었다면 무심코 넘어갔을 부분을 다양한 사람들이 모여 다양한 시각에서 바라보고 자신의 단어로 토론하고 이해하는걸 목표로 합니다. 22 | 23 | 그렇기 때문에 스터디때 질문하지 않고 의견을 내지 않는건 나 뿐만 아니라 스터디원 전체의 목표에 어긋나는 일이란걸 인지하고 "이런걸 물어봐도 되나?" 걱정하지 말고 마구마구 얘기하면 좋습니다. 24 | 25 | 그래서 우리 스터디에선 멍청한 질문은 없습니다. 모든 질문이 문제를 바라보는 새로운 관점일 뿐~ 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## HTTP Ignore file ## 2 | 3 | # Parcel 4 | .cache 5 | dist 6 | node_modules 7 | build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # http 2 | TCP 북 스터디 3 | 4 | ### 범위 5 | 다음 책들을 반복해서 읽어나가요. 6 | 7 | ##### 스터디에서 읽을 책. 8 | - 클린코드 9 | - 객체지향의 사실과 오해 10 | - 리팩터링 11 | - TDD 12 | - 오브젝트 13 | - GoF의 디자인 패턴 14 | - DDD 15 | - 클린아키텍처 16 | 17 | ##### 개인적으로 곁들여 읽으면 좋은 책. 18 | - 소프트웨어 장인 19 | - 실용주의 프로그래머 20 | - 맨먼스 미신 21 | - 익스트림 프로그래밍 22 | - 코딩 호러가 들려주는 진짜 소프트웨어 개발 이야기 23 | - 코딩 호러의 이펙티브 프로그래밍 24 | - 조엘 온 소프트웨어 25 | 26 | ### ❓ 진행방식 27 | - 매주 일요일 오후9시 디스코드에서 만나 온라인으로 진행합니다. 28 | - 스터디 동안에는 마이크를 끄지 않습니다. 29 | - 정해진 분량을 각자 읽고, 이해가 되지 않는 부분이나 저자의 의견에 동의가 되지 않는 부분, 혹은 다른 사람의 생각이 궁금한 부분을 코멘트로 남깁니다. 30 | - 한명의 드라이버를 정하고, 드라이버는 해당 코멘트들을 소개하고 토론을 진행하는 역할을 맡습니다. 31 | - 여느 스터디와 같이 책 내용을 요약하고 발표하거나 하진 않아요. 32 | 33 | 34 | ### ❗ 우리 스터디에 멍청한 질문은 없습니다 35 | - 내가 이해 안 간 부분이 있다면 그건 높은 확률로 다른 사람도 이해를 못했을 것입니다. 36 | - 우리 스터디는 이걸 위해서 진행합니다. 37 | - 혼자서 읽었다면 무심코 넘어갔을 부분을 다양한 사람들이 모여 다양한 시각에서 바라보고 자신의 단어로 토론하고 이해하는걸 목표로 합니다. 38 | - 그렇기 때문에 스터디때 질문하지 않고 의견을 내지 않는건 나 뿐만 아니라 스터디원 전체의 목표에 어긋나는 일이란걸 인지하고 39 | - "이런걸 물어봐도 되나?" 걱정하지 말고 마구마구 얘기하면 좋습니다. 40 | - 그래서 우리 스터디에선 멍청한 질문은 없습니다. 모든 질문이 문제를 바라보는 새로운 관점일 뿐 41 | -------------------------------------------------------------------------------- /pickme/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | CSS 13 | 14 | 15 | HTML 16 | 17 | 18 | Probable bugsCSS 19 | 20 | 21 | RELAX NG 22 | 23 | 24 | SQL 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /pickme/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pickme/.idea/pickme.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pickme/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pickme/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 6 | `, 7 | componentClassName: 'set-num-of-participant-component', 8 | eventHandler: Function.prototype, 9 | getComponentHtml() { 10 | return this.componentHtml; 11 | }, 12 | getComponentClassName() { 13 | return this.componentClassName; 14 | }, 15 | setEventHandler(eventHandler) { 16 | this.eventHandler = eventHandler; 17 | }, 18 | increaseNumOfParticipant() { 19 | return this.eventHandler(); 20 | }, 21 | addEvent() { 22 | const button = document.getElementsByClassName('set-num-of-participant-component__num-submit-button')[0]; 23 | button.addEventListener('click', this.increaseNumOfParticipant.bind(this)); 24 | }, 25 | render(eventHandler) { 26 | this.setEventHandler(eventHandler); 27 | domHelper.render(this); 28 | }, 29 | }; 30 | 31 | export default increaseParticipantButtonComponent; 32 | -------------------------------------------------------------------------------- /pickme/KangJiJi/src/components/pickPresenterButtonComponent.js: -------------------------------------------------------------------------------- 1 | import domHelper from '../helper/domHelper'; 2 | 3 | const pickPresenterButtonComponent = { 4 | componentHtml: '', 5 | componentClassName: 'pick-presenter-component', 6 | eventHandler: Function.prototype, 7 | getComponentHtml() { 8 | return this.componentHtml; 9 | }, 10 | getComponentClassName() { 11 | return this.componentClassName; 12 | }, 13 | setEventHandler(eventHandler) { 14 | this.eventHandler = eventHandler; 15 | }, 16 | pickPresenter() { 17 | return this.eventHandler(); 18 | }, 19 | addEvent() { 20 | const button = document.getElementsByClassName('pick-presenter-component__pick-presenter-button')[0]; 21 | button.addEventListener('click', this.pickPresenter.bind(this)); 22 | }, 23 | render(eventHandler) { 24 | this.setEventHandler(eventHandler); 25 | domHelper.render(this); 26 | }, 27 | }; 28 | 29 | export default pickPresenterButtonComponent; 30 | -------------------------------------------------------------------------------- /pickme/KangJiJi/src/components/setInformationOfParticipantComponent.js: -------------------------------------------------------------------------------- 1 | import domHelper from '../helper/domHelper'; 2 | 3 | const setInformationOfParticipantComponent = { 4 | componentHtml: `
5 | 6 | 7 |
`, 8 | componentClassName: 'set-information-of-participant-component', 9 | eventHandler: Function.prototype, 10 | getComponentHtml() { 11 | return this.componentHtml; 12 | }, 13 | getComponentClassName() { 14 | return this.componentClassName; 15 | }, 16 | setEventHandler(eventHandler) { 17 | this.eventHandler = eventHandler; 18 | }, 19 | getInformationOfParticipants() { 20 | const informationOfParticipantWrapperDom = document.getElementsByClassName(this.getComponentClassName())[0]; 21 | const informationOfParticipantDom = Array.from(informationOfParticipantWrapperDom.children); 22 | const informationOfParticipants = informationOfParticipantDom.map(function getInformationOfParticipantFromDom(dom) { 23 | const informationOfParticipant = [dom.children[0].value, Number(dom.children[1].value)]; 24 | return informationOfParticipant; 25 | }); 26 | return informationOfParticipants; 27 | }, 28 | appendComponent() { 29 | domHelper.render(this, this.getComponentClassName()); 30 | }, 31 | addEvent() { }, 32 | render(eventHandler) { 33 | this.setEventHandler(eventHandler); 34 | domHelper.render(this); 35 | }, 36 | }; 37 | 38 | export default setInformationOfParticipantComponent; 39 | -------------------------------------------------------------------------------- /pickme/KangJiJi/src/components/showPresenterComponent.js: -------------------------------------------------------------------------------- 1 | import domHelper from '../helper/domHelper'; 2 | 3 | const showPresenterComponent = { 4 | componentHtml: '
', 5 | componentClassName: 'show-presenter-component', 6 | eventHandler: Function.prototype, 7 | getComponentHtml() { 8 | return this.componentHtml; 9 | }, 10 | getComponentClassName() { 11 | return this.componentClassName; 12 | }, 13 | setEventHandler(eventHandler) { 14 | this.eventHandler = eventHandler; 15 | }, 16 | setPresenter(presenter) { 17 | document.getElementsByClassName('show-presenter-component__presenter-name')[0].innerHTML = presenter; 18 | }, 19 | addEvent() { }, 20 | render(eventHandler) { 21 | this.setEventHandler(eventHandler); 22 | domHelper.render(this); 23 | }, 24 | }; 25 | 26 | export default showPresenterComponent; 27 | -------------------------------------------------------------------------------- /pickme/KangJiJi/src/helper/domHelper.js: -------------------------------------------------------------------------------- 1 | const domHelper = { 2 | render(component, parentClassName) { 3 | this.appendHtml(component, parentClassName) 4 | .then(this.addEvent) 5 | .catch(function errorHandling(err) { 6 | console.log('Error: ', err); 7 | }); 8 | }, 9 | appendHtml(component, parentClassName) { 10 | return new Promise(function appendHtml(resolve, reject) { 11 | if (!component.getComponentHtml) { 12 | reject(new Error('This component does not have getComponentHtml method')); 13 | } 14 | 15 | const parentComponent = parentClassName 16 | ? document.getElementsByClassName(parentClassName)[0] 17 | : document.getElementById('app'); 18 | 19 | const divWrapper = document.createElement('div'); 20 | divWrapper.className = component.getComponentClassName(); 21 | divWrapper.innerHTML = component.getComponentHtml(); 22 | 23 | if (!parentClassName) { 24 | parentComponent.appendChild(divWrapper); 25 | } else { 26 | parentComponent.appendChild(divWrapper.children[0]); 27 | } 28 | 29 | resolve(component); 30 | }); 31 | }, 32 | addEvent(component) { 33 | return new Promise(function addEvent(resolve, reject) { 34 | if (!component.addEvent) { 35 | reject(new Error('This component does not have addEvent method')); 36 | } 37 | component.addEvent(); 38 | resolve(component); 39 | }); 40 | }, 41 | removeHtml() { 42 | console.log('This function remove component by class name'); 43 | }, 44 | initDom() { 45 | document.getElementById('app').innerHTML = ''; 46 | }, 47 | }; 48 | 49 | export default domHelper; 50 | -------------------------------------------------------------------------------- /pickme/KangJiJi/src/helper/presenterPickHelper.js: -------------------------------------------------------------------------------- 1 | const presenterPickHelper = { 2 | pickPresenter(informationOfParticipants) { 3 | const informationOfParticipantsWithPercentage = this.anotherEqualityCalculatePercentage(informationOfParticipants); 4 | const presenter = this.randPickWithPercentage(informationOfParticipantsWithPercentage); 5 | return presenter; 6 | }, 7 | equalityCalculatePercentage(informationOfParticipants) { 8 | let equalityPercent = 0; 9 | const lengthOfInformationOfParticipants = informationOfParticipants.length; 10 | const sortedInformationOfParticipants = [...informationOfParticipants].sort(function descSort(a, b) { 11 | return b[1] - a[1]; 12 | }); 13 | 14 | equalityPercent = 100 / lengthOfInformationOfParticipants; 15 | 16 | sortedInformationOfParticipants.forEach(function changePresentationTimesToPercentage(informationOfParticipant) { 17 | informationOfParticipant[1] = equalityPercent; 18 | }); 19 | return sortedInformationOfParticipants; 20 | }, 21 | anotherEqualityCalculatePercentage(informationOfParticipants) { 22 | const sortedInformationOfParticipants = [...informationOfParticipants].sort(function ascSort(a, b) { 23 | return a[1] - b[1]; 24 | }); 25 | 26 | sortedInformationOfParticipants.forEach(function changePresentationTimesToPercentage(informationOfParticipant, index) { 27 | if (index === 0) { 28 | informationOfParticipant[1] = 100; 29 | } else { 30 | informationOfParticipant[1] = 0; 31 | } 32 | }); 33 | 34 | return sortedInformationOfParticipants; 35 | }, 36 | randPickWithPercentage(informationOfParticipants) { 37 | const listLength = informationOfParticipants.length; 38 | const rand = Math.floor(Math.random() * 100) + 1; 39 | let sumOfPercentage = 0; 40 | 41 | for (let i = 0; i < listLength; i += 1) { 42 | sumOfPercentage += informationOfParticipants[i][1]; 43 | if (rand <= sumOfPercentage) { 44 | return informationOfParticipants[i][0]; 45 | } 46 | } 47 | 48 | return false; 49 | }, 50 | }; 51 | 52 | export default presenterPickHelper; 53 | -------------------------------------------------------------------------------- /pickme/KangJiJi/src/pages/mainPage.js: -------------------------------------------------------------------------------- 1 | import increaseParticipantButtonComponent from '../components/increaseParticipantButtonComponent'; 2 | import setInformationOfParticipantComponent from '../components/setInformationOfParticipantComponent'; 3 | import pickPresenterButtonComponent from '../components/pickPresenterButtonComponent'; 4 | import showPresenterComponent from '../components/showPresenterComponent'; 5 | import presenterPickHelper from '../helper/presenterPickHelper'; 6 | 7 | const mainPage = { 8 | appendSetInformationOfParticipantComponent() { 9 | setInformationOfParticipantComponent.appendComponent(); 10 | }, 11 | increaseParticipant() { 12 | this.appendSetInformationOfParticipantComponent(); 13 | }, 14 | showPresenter(presenterName) { 15 | showPresenterComponent.setPresenter(presenterName); 16 | }, 17 | pickPresenter() { 18 | const informationOfParticipants = setInformationOfParticipantComponent.getInformationOfParticipants(); 19 | const presenterName = presenterPickHelper.pickPresenter(informationOfParticipants); 20 | this.showPresenter(presenterName); 21 | }, 22 | render() { 23 | increaseParticipantButtonComponent.render(this.increaseParticipant.bind(this)); 24 | setInformationOfParticipantComponent.render(); 25 | pickPresenterButtonComponent.render(this.pickPresenter.bind(this)); 26 | showPresenterComponent.render(); 27 | }, 28 | }; 29 | 30 | export default mainPage; 31 | -------------------------------------------------------------------------------- /pickme/hyesun03/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # celery beat schedule file 95 | celerybeat-schedule 96 | 97 | # SageMath parsed files 98 | *.sage.py 99 | 100 | # Environments 101 | .env 102 | .venv 103 | env/ 104 | venv/ 105 | ENV/ 106 | env.bak/ 107 | venv.bak/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | .spyproject 112 | 113 | # Rope project settings 114 | .ropeproject 115 | 116 | # mkdocs documentation 117 | /site 118 | 119 | # mypy 120 | .mypy_cache/ 121 | .dmypy.json 122 | dmypy.json 123 | 124 | # Pyre type checker 125 | .pyre/ 126 | 127 | ### macOS template 128 | # General 129 | .DS_Store 130 | .AppleDouble 131 | .LSOverride 132 | 133 | # Icon must end with two \r 134 | Icon 135 | 136 | # Thumbnails 137 | ._* 138 | 139 | # Files that might appear in the root of a volume 140 | .DocumentRevisions-V100 141 | .fseventsd 142 | .Spotlight-V100 143 | .TemporaryItems 144 | .Trashes 145 | .VolumeIcon.icns 146 | .com.apple.timemachine.donotpresent 147 | 148 | # Directories potentially created on remote AFP share 149 | .AppleDB 150 | .AppleDesktop 151 | Network Trash Folder 152 | Temporary Items 153 | .apdisk 154 | 155 | ### JetBrains template 156 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 157 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 158 | 159 | # User-specific stuff 160 | .idea/**/workspace.xml 161 | .idea/**/tasks.xml 162 | .idea/**/usage.statistics.xml 163 | .idea/**/dictionaries 164 | .idea/**/shelf 165 | 166 | # Generated files 167 | .idea/**/contentModel.xml 168 | 169 | # Sensitive or high-churn files 170 | .idea/**/dataSources/ 171 | .idea/**/dataSources.ids 172 | .idea/**/dataSources.local.xml 173 | .idea/**/sqlDataSources.xml 174 | .idea/**/dynamic.xml 175 | .idea/**/uiDesigner.xml 176 | .idea/**/dbnavigator.xml 177 | 178 | # Gradle 179 | .idea/**/gradle.xml 180 | .idea/**/libraries 181 | 182 | # Gradle and Maven with auto-import 183 | # When using Gradle or Maven with auto-import, you should exclude module files, 184 | # since they will be recreated, and may cause churn. Uncomment if using 185 | # auto-import. 186 | # .idea/modules.xml 187 | # .idea/*.iml 188 | # .idea/modules 189 | # *.iml 190 | # *.ipr 191 | 192 | # CMake 193 | cmake-build-*/ 194 | 195 | # Mongo Explorer plugin 196 | .idea/**/mongoSettings.xml 197 | 198 | # File-based project format 199 | *.iws 200 | 201 | # IntelliJ 202 | out/ 203 | 204 | # mpeltonen/sbt-idea plugin 205 | .idea_modules/ 206 | 207 | # JIRA plugin 208 | atlassian-ide-plugin.xml 209 | 210 | # Cursive Clojure plugin 211 | .idea/replstate.xml 212 | 213 | # Crashlytics plugin (for Android Studio and IntelliJ) 214 | com_crashlytics_export_strings.xml 215 | crashlytics.properties 216 | crashlytics-build.properties 217 | fabric.properties 218 | 219 | # Editor-based Rest Client 220 | .idea/httpRequests 221 | 222 | # Android studio 3.1+ serialized cache file 223 | .idea/caches/build_file_checksums.ser 224 | 225 | -------------------------------------------------------------------------------- /pickme/hyesun03/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /pickme/hyesun03/.idea/hyesun03.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /pickme/hyesun03/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | CSS 13 | 14 | 15 | HTML 16 | 17 | 18 | Probable bugsCSS 19 | 20 | 21 | RELAX NG 22 | 23 | 24 | SQL 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /pickme/hyesun03/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pickme/hyesun03/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pickme/hyesun03/crawling.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | import requests 3 | import json 4 | 5 | from bs4 import BeautifulSoup as bs 6 | 7 | 8 | def crawl_roll_book() -> Dict: 9 | r = requests.get("https://github.com/TeamCrazyPerformance/http/wiki/%5BGoF-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4%5D-%EC%B6%9C%EC%84%9D%EB%B6%80") 10 | 11 | content = bs(r.text, 'html.parser') 12 | 13 | table = content.find('table') 14 | thead = table.select('thead > tr > th') 15 | names = [item.text for item in thead][1:] 16 | 17 | data = {"members": []} 18 | members = data["members"] 19 | for i in range(len(names)): 20 | members.append({"name": names[i], "speak": [], "wiki": [], "absent": []}) 21 | 22 | tbody = table.select('tbody > tr') 23 | 24 | for row in tbody: 25 | row = row.select('tr > td') 26 | date = row[0].text.replace('.', '-') 27 | row = row[1:] 28 | 29 | for i in range(len(names)): 30 | if row[i].text == 'X': 31 | members[i]["absent"].append(date) 32 | 33 | return data 34 | 35 | 36 | def to_json(data: Dict) -> None: 37 | with open('crawling_test.json', 'w', encoding='utf-8') as file: 38 | json.dump(data, file, ensure_ascii=False, indent='\t') 39 | 40 | 41 | if __name__ == '__main__': 42 | data = crawl_roll_book() 43 | to_json(data) 44 | 45 | with open("crawling_test.json") as file: 46 | data = json.load(file) 47 | study_member = data["members"] 48 | 49 | # study_member.json과 똑같은 포맷으로 만들어진 것을 볼 수 있음 50 | print(study_member) 51 | -------------------------------------------------------------------------------- /pickme/hyesun03/crawling_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "members": [ 3 | { 4 | "name": "강지훈", 5 | "speak": [], 6 | "wiki": [], 7 | "absent": [] 8 | }, 9 | { 10 | "name": "김일범", 11 | "speak": [], 12 | "wiki": [], 13 | "absent": [ 14 | "19-07-14", 15 | "19-09-08" 16 | ] 17 | }, 18 | { 19 | "name": "김희라", 20 | "speak": [], 21 | "wiki": [], 22 | "absent": [] 23 | }, 24 | { 25 | "name": "윤지수", 26 | "speak": [], 27 | "wiki": [], 28 | "absent": [] 29 | }, 30 | { 31 | "name": "이건우", 32 | "speak": [], 33 | "wiki": [], 34 | "absent": [] 35 | }, 36 | { 37 | "name": "이재란", 38 | "speak": [], 39 | "wiki": [], 40 | "absent": [ 41 | "19-07-07", 42 | "19-08-04", 43 | "19-09-01" 44 | ] 45 | }, 46 | { 47 | "name": "최혜선", 48 | "speak": [], 49 | "wiki": [], 50 | "absent": [] 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /pickme/hyesun03/main.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | import json 3 | import random 4 | import click 5 | 6 | 7 | def read_json() -> List[list]: 8 | """스터디 참석자에 대한 정보가 담긴 json 파일을 읽어옵니다.""" 9 | 10 | with open("study_member.json") as file: 11 | data = json.load(file) 12 | study_member = data["members"] 13 | 14 | name, speak, wiki, absent = [], [], [], [] 15 | for member in study_member: 16 | name.append(member["name"]) 17 | speak.append(len(member["speak"])) 18 | wiki.append(len(member["wiki"])) 19 | absent.append(len(member["absent"])) 20 | 21 | return [name, speak, wiki, absent] 22 | 23 | 24 | @click.command() 25 | @click.option('--today_absent', prompt="오늘 결석한 사람들", help="발표자와 서기를 뽑습니다.") 26 | def pickme(today_absent: str) -> None: 27 | """오늘 결석자에 대한 정보를 인자로 받고, 발표자와 서기를 뽑습니다.""" 28 | 29 | name, speak, wiki, absent = read_json() 30 | today_absent = today_absent.strip().split() 31 | 32 | if len(today_absent) > len(name) - 2: 33 | print("<< 축 하 합 니 다 >>") 34 | print("오늘은 스터디 쉬는날!") 35 | print("<< 축 하 합 니 다 >>") 36 | return 37 | 38 | # 현재까지 진행한 스터디 횟수 == 발표 횟수 39 | total = len(speak) 40 | 41 | # 1 - ((내가 걸린 횟수 - 내가 결석한 횟수) / 여태 진행했던 횟수) 로 가중치 적용 42 | speak = [1 - (speak[i] - absent[i]) / total for i in range(len(name))] 43 | wiki = [1 - (wiki[i] - absent[i]) / total for i in range(len(name))] 44 | 45 | # 1회차 뽑기 46 | today_speaker = random.choices(name, speak, k=1)[0] 47 | today_wikier = random.choices(name, wiki, k=1)[0] 48 | 49 | # 오늘 결석자면 제외, 발표자와 서기가 겹치는것도 제외 50 | # choices는 리스트를 반환하며 k=1은 한명만 뽑겠다는 뜻 51 | while today_speaker in today_absent: 52 | today_speaker = random.choices(name, wiki, k=1)[0] 53 | 54 | while today_wikier in today_absent or today_wikier == today_speaker: 55 | today_wikier = random.choices(name, wiki, k=1)[0] 56 | 57 | print("<< 축 하 합 니 다 >>") 58 | print("발표자:\t", today_speaker) 59 | print("서기:\t", today_wikier) 60 | print("<< 축 하 합 니 다 >>") 61 | 62 | 63 | if __name__ == '__main__': 64 | pickme() 65 | -------------------------------------------------------------------------------- /pickme/hyesun03/requirements.txt: -------------------------------------------------------------------------------- 1 | Click==7.0 2 | -------------------------------------------------------------------------------- /pickme/hyesun03/study_member.json: -------------------------------------------------------------------------------- 1 | { 2 | "members": [ 3 | { 4 | "name": "최혜선", 5 | "speak": [ 6 | "2019-09-01" 7 | ], 8 | "wiki": [ 9 | "2019-08-25" 10 | ], 11 | "absent": [ 12 | "2019-09-08", 13 | "2019-09-15", 14 | "2019-09-22" 15 | ] 16 | }, 17 | { 18 | "name": "강지훈", 19 | "speak": [ 20 | ], 21 | "wiki": [ 22 | "2019-09-01", 23 | "2019-09-08" 24 | ], 25 | "absent": [ 26 | ] 27 | }, 28 | { 29 | "name": "김희라", 30 | "speak": [ 31 | "2019-08-25" 32 | ], 33 | "wiki": [ 34 | ], 35 | "absent": [ 36 | ] 37 | }, 38 | { 39 | "name": "이건우", 40 | "speak": [ 41 | "2019-09-08", 42 | "2019-09-15", 43 | "2019-09-22" 44 | ], 45 | "wiki": [ 46 | ], 47 | "absent": [ 48 | ] 49 | }, 50 | { 51 | "name": "이사빈", 52 | "speak": [ 53 | ], 54 | "wiki": [ 55 | "2019-09-15" 56 | ], 57 | "absent": [ 58 | ] 59 | }, 60 | { 61 | "name": "이재란", 62 | "speak": [ 63 | ], 64 | "wiki": [ 65 | "2019-09-22" 66 | ], 67 | "absent": [ 68 | ] 69 | }, 70 | { 71 | "name": "김일범", 72 | "speak": [ 73 | ], 74 | "wiki": [ 75 | ], 76 | "absent": [ 77 | ] 78 | }, 79 | { 80 | "name": "윤지수", 81 | "speak": [ 82 | ], 83 | "wiki": [ 84 | ], 85 | "absent": [ 86 | ] 87 | } 88 | ] 89 | } -------------------------------------------------------------------------------- /pickme/hyesun03/test.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | 4 | 5 | with open("study_member.json") as file: 6 | data = json.load(file) 7 | study_member = data["members"] 8 | 9 | name, speak, wiki, absent = [], [], [], [] 10 | for member in study_member: 11 | name.append(member["name"]) 12 | speak.append(len(member["speak"])) 13 | wiki.append(len(member["wiki"])) 14 | absent.append(len(member["absent"])) 15 | 16 | total = len(speak) 17 | 18 | speak = [1 - (speak[i] - absent[i]) / total for i in range(8)] 19 | wiki = [1 - (wiki[i] - absent[i]) / total for i in range(8)] 20 | 21 | today_absent = ["윤지수"] 22 | 23 | test1, test2 = {}, {} 24 | for _ in range(100000): 25 | today_speaker = random.choices(name, speak, k=1)[0] 26 | today_wikier = random.choices(name, wiki, k=1)[0] 27 | 28 | while today_speaker in today_absent: 29 | today_speaker = random.choices(name, wiki, k=1)[0] 30 | 31 | while today_wikier in today_absent or today_wikier == today_speaker: 32 | today_wikier = random.choices(name, wiki, k=1)[0] 33 | 34 | if today_speaker in test1: 35 | test1[today_speaker] += 1 36 | else: 37 | test1[today_speaker] = 1 38 | 39 | if today_wikier in test2: 40 | test2[today_wikier] += 1 41 | else: 42 | test2[today_wikier] = 1 43 | 44 | # 많이 당첨이 되지 않았거나, 많이 결석하면 더 많이 걸리는 경향이 보임 45 | print("발표", test1) 46 | print("서기", test2) 47 | -------------------------------------------------------------------------------- /pickme/jisoo/index.html: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /pickme/jisoo/spec.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 당일 스터디 발표를 진행할 두명 (1발표자, 1서기)을 '랜덤'하게 뽑습니다. 3 | 최근 발표 혹은 누적 발표횟수에 따라 가중치를 부여하여 '랜덤'하게 뽑습니다. 4 | 발표와 서기는 별개로 누적되어야 합니다. 5 | 6 | ## Spec 7 | 8 | 1. 최소한의 설정으로 실행가능해야 함 9 | 2. 최근 발표/작성 일에 따라 가중치를 다르게 랜덤하게 뽑아야 함. 10 | 3. 결석자에 대한 적절한 처리가 필요함. 11 | 4. 테스트를 통해 '랜덤' 하게 선출된다는 검증이 필요함. 12 | --------------------------------------------------------------------------------