<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Narongdej Sarnsuwan</title>
    <description>The latest articles on Forem by Narongdej Sarnsuwan (@narongdejsrn).</description>
    <link>https://forem.com/narongdejsrn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F175095%2Fdaf3a408-8f8a-4119-81d8-04eeef8b89b8.jpeg</url>
      <title>Forem: Narongdej Sarnsuwan</title>
      <link>https://forem.com/narongdejsrn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/narongdejsrn"/>
    <language>en</language>
    <item>
      <title>Real-world inch in CSS</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Mon, 05 Oct 2020 17:00:00 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/real-world-inch-in-css-5g7k</link>
      <guid>https://forem.com/narongdejsrn/real-world-inch-in-css-5g7k</guid>
      <description>&lt;p&gt;In web development, a CSS inch is not the same across all devices. That’s because every screen has a different PPI or Pixel Per Inch.&lt;/p&gt;

&lt;p&gt;1 inch in CSS equal 96px. That 1 inch will only be equal to 1 real-world inch that you can find in your typical ruler if your screen is exactly 96 dpi. For example, a 23” monitor with 1920 x 1080 resolution will have about 96 PPI while an iPhone 11 Pro Max with a much smaller screen, but with much higher resolution will have 456 PPI. If you draw a 1 CSS inch line or 96px, it will much shorter on the iPhone than on the 23 inch monitor.&lt;/p&gt;

&lt;p&gt;So can you make your web display the 1 CSS inch line as a real-life inch across all devices? The answer is you can, kinda. Cause you’ll need to know the screen PPI, and the browser cannot give you the monitor PPI.&lt;/p&gt;

&lt;p&gt;However, you can predict the PPI using the User Agent (ex. iPhone 11 Pro Max) or, just straight ask the user. &lt;a href="https://www.calculatorsoup.com/calculators/technology/ppi-calculator.php"&gt;To calculate the PPI, you’ll need the screen diagonal in inch and diagonal in pixel. Then divide the diagonal in pixel with diagonal an inch to get the PPI.&lt;/a&gt; (or just use &lt;a href="https://dpi.lv/"&gt;dpi.lv&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;After that, you’ll be able to adjust the line size accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var node = document.querySelector('#line')
node.style.width = (node.clientWidth / 96.0) * (&amp;lt;&amp;lt;the_screen_dpi&amp;gt;&amp;gt; / window.devicePixelRatio)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio"&gt;window.devicePixelRatio&lt;/a&gt; returns the size of one CSS pixel to the size of one physical pixel. For example, for the retina display, this number will be 2.&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>วิธีการทำ PDF Extraction ด้วย Python</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Fri, 25 Sep 2020 17:00:00 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/pdf-extraction-python-1jln</link>
      <guid>https://forem.com/narongdejsrn/pdf-extraction-python-1jln</guid>
      <description>&lt;p&gt;ในยุคที่ข้อมูลเป็นสิ่งที่มีค่าสูง หากคนมีข้อมูลเยอะ คุณสามารถเอามาทำ Data Analysis เพื่อวางแผนการตลาด หรือคุณสามารถเอามาทำ Data Science เพื่อผลิตเครื่องมือใหม่ๆ แต่สิ่งที่คุณต้องมีให้ได้ก่อน นั่นคือมี Data&lt;/p&gt;

&lt;p&gt;วันนี้ผมจะมาพูดถึงเครื่องมือการ Extract ข้อมูลจากไฟล์ PDF ที่เป็นวิธีการเก็บไฟล์ที่นิยมของหลายๆบริษัท ผมจะแค่เกริ่นสั้นๆ ถึงวิธีการ extract ต่างๆ แต่จะยังไม่ลงลึก วิธีการทำใน blog นี้ ถ้าหากคุณสนใจการ extract วิธีไหนที่กล่าวใน blog นี้ สามารถเขียน comment ทิ้งไว้ได้ครับ แล้วผมจะพยายามเขียน blog อธิบายลงลึกอีกที&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured Data vs Non Structured Data
&lt;/h2&gt;

&lt;p&gt;ก่อนอื่นเราต้องมารู้จักกับสิ่งที่เรียกว่า Structured Data กับสิ่งที่เรียกว่า Non Structured Data กันก่อน&lt;/p&gt;

&lt;p&gt;Data สามารถแบ่งออกเป็นสองประเภทนี้&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured Data&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ข้อมูลที่เก็บใน relational database เช่น Postgres, MySQL หรือที่สามารถแสดงเป็น rows, column ได้&lt;/li&gt;
&lt;li&gt;ข้อมูลเป็นประเภท ตัวเลข วันที่ และตัวหนังสือ&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Unstructured Data&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ไม่สามารถแสดงเป็น rows, column และไม่ได้เก็บอยู่ใน relational database&lt;/li&gt;
&lt;li&gt;เป็นไฟล์จำพวก รูปภาพ คลิปเสียง วีดีโอ ไฟล์ Word, Email, Spreadsheet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ข้อเสียหลักๆของ unstructured data คือการนำออกมาใช้ยาก เพราะฉะนั้นหน้าที่หลักๆของการทำ extraction คือ&lt;em&gt;การแปลง unstructured data ให้กลายเป็น structured ให้ได้&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Searchable and Non-Seachable
&lt;/h2&gt;

&lt;p&gt;PDF หรือ Portable Document Format เป็นประเภทไฟล์ที่ Adobe สร้างขึ้น ใน PDF นั้นสามารถมี text, image, vector graphic, image และอื่นๆอีกมากมาย&lt;/p&gt;

&lt;p&gt;ส่วนใหญ่เวลาเราทำ extraction ข้อมูลที่เราต้องการเอาออกมาก็คือ ตัวหนังสือ หรือตัวเลขที่อยู่ในไฟล์ PDF&lt;/p&gt;

&lt;p&gt;ถ้าเราโชคดี ในไฟล์ PDF นั้น text ที่เราจะดึงออกมาจะถูกเก็บแบบเป็น “Text data” ซึ่งสามารถดึงออกมาได้ง่ายและแม่นยำ วิธีการเช็คง่ายๆ ก็ลองเปิดไฟล์ PDF และลาก text ดู ว่าสามารถ highlight text ได้ไหม ผมเรียกไฟล์เหล่านี้ว่า Searchable PDF&lt;/p&gt;

&lt;p&gt;ไฟล์อีกประเภทนึง คือไฟล์ที่เหมือนจะเป็นไฟล์ text ที่แปลงมาจากโปรแกรมอื่น แต่จริงๆแล้วเป็นไฟล์ image ที่ถูกแสกนขึ้นมา ทำให้เราไม่สามารถไปอ่าน Text Data ได้ เพราะมันคือรูปภาพ ผมเรียกไฟล์ประเภทนี้ว่า Non-Searchable PDF&lt;/p&gt;

&lt;p&gt;⚠️ จงระวัง Fake Searchable หรือ ชื่อที่ผมใช้เรียกไฟล์ที่ถูกแปลงจาก Non-Searchable ให้กลายเป็น Searchable ด้วยโปรแกรมเช่น Acrobat Pro&lt;/p&gt;

&lt;p&gt;เท่าที่เจอมา ข้อมูลที่ถูกแปลงมาจะนำมาใช้งานไม่ได้เลย แนะนำให้ลองลากและ copy text ออกมาดูว่า มันใช้งานได้ไหม ถ้าใช้ไม่ได้ อาจจะเป็นไฟล์แบบ Fake Searchable&lt;/p&gt;

&lt;h2&gt;
  
  
  Free Searchable PDF Extraction
&lt;/h2&gt;

&lt;p&gt;การ Extract ข้อมูลจากไฟล์ Searchable นั้น ผมแนะนำให้ลองไปใช้&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/jsvine/pdfplumber"&gt;PDF Plumber&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PDF Plumber สามารถ extract text, images, และแม้กระทั่งข้อมูลที่อยู่ใน format ของ table และนำออกมาเป็น DataFrame ให้ได้เลย หากข้อมูลที่ extract ไม่ถูกต้อง ให้ลองไปปรับ config ต่างๆที่อยู่ใน README ดู ผมเชื่อว่าสำหรับไฟล์ส่วนใหญ่ คุณสามารถ extract ด้วย PDF Plumber ได้เลย โดยแทบไม่ต้องทำอะไรเพิ่มเติมเลย&lt;/p&gt;

&lt;p&gt;สำหรับไฟล์ประเภท Table ต่างๆ และใช้ PDF Plumber ก็ยังไม่เวิร์ค ผมแนะนำให้ไปลอง&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://camelot-py.readthedocs.io"&gt;Camelot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tabula-py.readthedocs.io/en/latest/"&gt;Tabula-py&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Free Non-searchable PDF Extraction
&lt;/h2&gt;

&lt;p&gt;สำหรับไฟล์ประเภท Non-Searchable นั่น การ extract ออกมานั้นยากกว่า Searchable เยอะเลย คุณต้องใช้ OCR (Optical Character Recognition) เพื่อแปลงตัวอักษรและตัวเลขจากภาพ ให้กลายเป็น “Text data”&lt;/p&gt;

&lt;p&gt;สำหรับข้อมูลที่คุณต้องการเป็น paragraph คุณสามารถแยกภาพ paragraph นั้นออกมา และโยนเข้าไปในโปรแกรม OCR ได้เลย&lt;/p&gt;

&lt;p&gt;แต่หลายๆครั้งข้อมูลที่คุณต้องการจะอยู่ใน table ถ้าคุณโยน table เข้า OCR เลย สิ่งที่คุณได้กลับมาจะเละตุ๋มเป๊ะ วิธีการที่ผมทำแล้วผมว่าเวิร์ค คือพยายามตัด text ออกออกมาเป็นเป็นกล่องๆ แล้วค่อยประกอบ table กลับมาใหม่ แต่การที่คุณจะทำอย่างนั้นได้คุณจะต้องมีความรู้เรื่อง Image Processing พอสมควร&lt;/p&gt;

&lt;p&gt;พวกหัวข้อที่เกี่ยวข้องคือพวกการทำ &lt;a href="https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html"&gt;Hough lines&lt;/a&gt; และการหา &lt;a href="https://docs.opencv.org/3.4/d4/d73/tutorial_py_contours_begin.html"&gt;Contours&lt;/a&gt; สองอย่างนี้จะช่วยให้คุณหาเส้นของ table ใน image ได้ เพื่อหาว่าจุดที่คุณจะต้องเอาออกมาอยู่ตรงไหน&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;เครื่องมือสำหรับ Image Processing / PDF Processing&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://imagemagick.org"&gt;imagemagick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opencv.org"&gt;OpenCV&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ghostscript.com"&gt;ghostscript&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;สิ่งที่สำคัญที่คุณควรจะตระหนักไว้ตลอดเวลาคือ ณ ปัจจุบัน OCR ไม่มีทางแปลงข้อมูลออกมาได้ถูก 100% เพราะฉะนั้นคุณต้องระวัง ว่าข้อมูลที่คุณต้องการนำออกมาใช้ สามารถมีการผิดได้บ้าง&lt;/p&gt;

&lt;p&gt;คุณสามารถลองใช้ OCR program เช่น &lt;a href="https://github.com/sirfz/tesserocr"&gt;tesserocr&lt;/a&gt; ที่เป็น wrapper ของ Tesseract หรือถ้าไม่เวิร์คก็ลอง&lt;a href="https://narongdej.dev/blog/th/2020/02/29/train-cnn-to-read-mnist-number-with-keras-and-plaidml"&gt;เขียน model OCR ขึ้นมาเอง&lt;/a&gt;ก็ได้นะครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  Commercial Tools
&lt;/h2&gt;

&lt;p&gt;นอกจากการใช้ tools ฟรี นั้นก็มี tools ที่เสียตังค์และสามารถใช้งานการ extract ได้ดีมากๆอยู่สองตัวที่ผมอยากแนะนำก็คือ&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://pdf.abbyy.com"&gt;ABBYY FineReader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/vision"&gt;Google Vision AI&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;สุดท้ายผมเชื่อว่ายังไม่มี tools ไหนที่เป็นเครื่องมือเดียวที่ทำได้ทุกอย่าง และอาจเป็นไปไม่ได้ด้วยซ้ำ เนื่องจากข้อมูลที่อยู่ใน PDF ได้นั้นหลากหลายมาก และแต่ละคนก็เก็บข้อมูลไม่เหมือนกัน สิ่งที่เราทำได้ก็คือพยายามเรียนรู้เครื่องมือที่เรามี และนำมาใช้ให้ถูกต้องครับ&lt;/p&gt;

</description>
      <category>thai</category>
      <category>pdfextraction</category>
      <category>python</category>
    </item>
    <item>
      <title>การประเมิน Software ให้ถูกต้องแบบ Uncle Bob</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Fri, 18 Sep 2020 17:00:00 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/software-uncle-bob-4a4e</link>
      <guid>https://forem.com/narongdejsrn/software-uncle-bob-4a4e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;วันนี้ผมจะมาสรุปสิ่งที่ลุง Bob พูดเกี่ยวกับการประเมินให้ถูกต้อง ในงาน YOW!2016 มาให้อ่านกันครับ สำหรับใครที่ต้องการ&lt;a href="https://www.youtube.com/watch?v=eisuQefYw_o"&gt;ดูวีดีโอตัวเต็มสามารถกดเข้าไปดูได้ที่นี้เลย&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. &lt;strong&gt;การประเมินที่ผิด จะทำให้คนไม่ไว้ใจเรา&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;เช่นถ้าเราประเมินงานว่าใช้เวลาสัก 5 วัน แปลว่าเราสัญญาว่าเราจะทำให้เสร็จภายใน 5 วัน ถ้าเสร็จไม่ทัน คนที่เราให้คำสัญญาก็จะไม่ไว้ใจเราอีกต่อไป หลังจากเราเรื่มประเมินผิดพลาด ก็จะไม่มีคนไว้ใจเราอีกแล้วไม่ว่าอะไรก็ตาม&lt;/p&gt;

&lt;h2&gt;
  
  
  2. การประเมินส่วนใหญ่ เป็นการประเมินที่แย่
&lt;/h2&gt;

&lt;p&gt;เพราะการประเมินเป็นสิ่งที่ยาก โดยเฉพาะการประเมินการเขียน software เช่นถ้าให้คุณประเมินเวลาในการผูกเชือกรองเท้า คุณอาจจะประเมินว่าผูกได้ในเวลาไม่ถึง 5 วินาที และการประเมินนั้นอาจจะถูกเป๊ะๆ แต่ถ้าให้คุณประเมินการเขียนวิธีการผูกเชือกรองเท้า คุณจะประเมินผิดแน่นอน การเขียนวิธีการทำอะไรสักอย่าง คือสิ่งที่โปรแกรมเมอร์อย่างเราทำ เรามักจะประเมินผิดอย่างน้อย 3 เท่า แม้ว่าเราจะรู้อยู่แล้วว่าเราจะประเมินผิดอย่างน้อย 3 เท่า 😂&lt;/p&gt;

&lt;h2&gt;
  
  
  3. บางทีเราก็ประเมินสูงไป
&lt;/h2&gt;

&lt;p&gt;เรามักจะประเมินอะไรที่ดูยาก และไม่รู้ว่าต้องทำยังไง ณ เวลานั้น สูงเกินไป แต่เมื่อลงมือทำจริงๆ อาจจะไม่ใช้เวลาเยอะขนาดนั้น&lt;/p&gt;

&lt;h2&gt;
  
  
  4. การประเมินที่ดีควรจะมีสามสิ่งนี้
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ซื่อสัตย์ -&lt;/strong&gt; ถ้ามันเป็นข่าวร้าย คุณต้องสามารถบอกข่าวร้ายได้ ไม่ว่าบอกไปแล้ว คุณจะโดนด่า หรือโดนดุ เพราะว่าถ้าคุณกล้าบอกข่าวร้าย แล้วมันถูก คุณจะสร้างความเชื่อใจให้กับคนที่คุณบอก&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ต้องแม่น -&lt;/strong&gt; เมื่อถึงเวลาที่คุณประเมิน มันจะต้องเสร็จตามที่คุณบอกจริงๆ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ต้องละเอียด ต้องเป๊ะ -&lt;/strong&gt; อย่าประเมินว่างานจะเสร็จภายในชีวิตนี้ และอย่าเป๊ะเกินไป &lt;em&gt;อย่าพยายามประเมินว่าจะเสร็จวันไหน ให้ประเมินว่าจะเสร็จช่วงวันไหน&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. การประเมินส่วนใหญ่ เป็นการโกหก
&lt;/h2&gt;

&lt;p&gt;ถ้าคุณประเมินว่างานจะเสร็จวันที่ … ส่วนใหญ่คุณก็ทำไม่เสร็จวันนั้นหรอก เพราะฉะนั้นคุณโกหก หรือว่าคุณทำเสร็จไปแล้ว แต่ประเมินว่าจะเสร็จอาทิตย์ คุณโกหก สาเหตุหนึ่งที่ทำให้คุณโกหก &lt;em&gt;เพราะว่าคุณประเมินจากหลังมาหน้า&lt;/em&gt; โปรเจ็คส่วนใหญ่คุณรู้อยู่แล้วว่า end date มันต้องอยู่วันไหน คุณถูกบีบมากว่ามันจะต้องเสร็จวันนี้ แล้วบ่อยครั้งที่ถ้าคุณประเมินให้ถูกต้องมันจะเกิน end date สิ่งที่คุณทำต่อไปก็คือโกหกว่ามันจะเสร็จ สิ่งที่คุณควรจะทำคือประเมินอย่างถูกต้อง แล้วเอามาแผ่ดูว่า features ไหนที่ตัดออกไปได้บ้าง หรืออันไหนที่ถ้าเพิ่มคนจะช่วยให้คุณทำเสร็จทัน หรือถ้ายังไงไม่เสร็จคุณก็ควรยืนกรานในสิ่งที่คุณประเมิน&lt;/p&gt;

&lt;h2&gt;
  
  
  6. การประเมินให้ถูกต้อง
&lt;/h2&gt;

&lt;p&gt;หากคุณต้องการประเมินให้ถูกต้อง ให้ทำ Work Breakdown Structure แค่ไม่กี่ level แล้วประเมินเวลา เสร็จแล้ว &lt;strong&gt;คูณจำนวนวันทั้งหมดด้วย 4 - 6&lt;/strong&gt; เพราะว่าการประเมินภาพรวมได้ไม่แม่น หากเราต้องการประเมินให้แม่นขึ้น เราต้องใช้เวลานานมาก และเวลาคือเงิน เพราะฉะนั้นทำ Work Breakdown แค่ 3 - 4 level พอ แล้วคูณ 4 - 6 ไปดีกว่า&lt;/p&gt;

&lt;p&gt;&lt;em&gt;เพราะถ้าคุณต้องการตัวเลขที่ถูกต้องจริงๆ คุณต้องทำโปรเจ็คนั้นไปแล้วจริงๆ&lt;/em&gt; &lt;strong&gt;หน้าที่คุณก็คือต้องบอกให้ manager รู้ด้วยเช่นกัน Cost ของการประเมินให้แม่นขึ้นโต exponential แต่สิ่งได้รับกลับมาเป็น logarithm 👈&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. สูตรการประเมิน
&lt;/h2&gt;

&lt;p&gt;ให้ประเมินเป็น 3 สถานการณ์&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ทุกอย่างเป็นไปได้สวย ( &lt;strong&gt;B&lt;/strong&gt; ) - โอกาสเกิดขึ้นแค่ 5%&lt;/li&gt;
&lt;li&gt;ทุกอย่างพังพินาศ ( &lt;strong&gt;W&lt;/strong&gt; ) - โอกาสเกิดขึ้น 95%&lt;/li&gt;
&lt;li&gt;ทุกอย่างตามที่คาดไว้ ( &lt;strong&gt;N&lt;/strong&gt; )- โอกาสเกิดขึ้น 50%&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ทุกครั้งที่คุณตอบการประเมินให้บอกเป็นช่วงเวลา คุณสามารถโชว์ graph ความน่าจะเป็นได้&lt;/p&gt;

&lt;p&gt;คุณสามารถเอาเลขที่คุณประเมินมาวาด normal distribution โดยใช้สูตรด้านล่าง เพื่อแสดงช่วงของการประเมิน&lt;/p&gt;

&lt;p&gt;stddev = (W-B)/6&lt;br&gt;
mean = (B+W+4N) / 6&lt;br&gt;
ProjectMean = sum(mean)&lt;br&gt;
ProjectStdDev = sqrt(sum(stddev^2))&lt;/p&gt;

&lt;h2&gt;
  
  
  8. หน้าที่ของ Manager คือการ Manage ความไม่แน่นอน
&lt;/h2&gt;

&lt;p&gt;ถ้าคุณให้การประเมินเป็นช่วงเวลาแบบด้านบน Manager จะไม่เอาแน่นอน เพราะมันคือหน้าที่ของเขาที่ต้องกำจัดความไม่แน่นอน แต่ก็ไม่เป็นไร สิ่งที่คุณต้องทำการคือยืนหยัดกับความจริง และ &lt;strong&gt;ห้ามให้สัญญา ถ้าคุณทำตามคำสัญญาไม่ได้&lt;/strong&gt; เพราะ Manager ก็จะประเมินงานอื่นๆตามคำที่คุณบอก ถ้าคุณประเมินผิดจะทำให้เกิด domino effect หรืองานพังต่อๆกันไป คุณต้องกล้าที่จะบอกว่า &lt;strong&gt;ไม่ ถ้ามันเป็นไปไม่ได้จริงๆ&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. โปรแกรมเมอร์ไม่ชอบการเผชิญหน้า
&lt;/h2&gt;

&lt;p&gt;เราชอบทำงานกับคอม คอมไม่เคยเถียงคุณ ไม่เคยโมโหคุณ เราไม่เข้ามาทำงานสายนี้เพราะชอบพบปะ ทำงาน หรือมีปัญหากับคน แต่ manager ที่ดีชอบ และการที่คนแบบ manager ได้ความจริง คือผ่านการเผชิญหน้ากับคน เพราะฉะนั้นถ้าคุณยอมให้ manager ชนะ โดยไม่เถียง คุณจะกลายเป็นคนโกหกเพราะไม่กล้าเผชิญหน้าโดยใช้ความจริง&lt;/p&gt;

</description>
      <category>thai</category>
      <category>estimation</category>
      <category>projectmanagement</category>
    </item>
    <item>
      <title>Missing 'window' variable in AppDelegate - CoreData</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Sat, 12 Sep 2020 15:32:14 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/missing-window-variable-in-appdelegate-coredata-4e5c</link>
      <guid>https://forem.com/narongdejsrn/missing-window-variable-in-appdelegate-coredata-4e5c</guid>
      <description>&lt;p&gt;If you're following along the &lt;strong&gt;"&lt;a href="https://developer.apple.com/documentation/coredata/setting_up_a_core_data_stack"&gt;Setting Up a Core Data Stack&lt;/a&gt;"&lt;/strong&gt; tutorial for Swift in apple website, and you got stuck on the &lt;code&gt;rootVC = window?.rootViewController&lt;/code&gt; part, then you're not alone.&lt;/p&gt;

&lt;p&gt;Since iOS 13 (Xcode 11) the life cycle of UIKit introduce UISceneDelegate, &lt;a href="https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle"&gt;read more here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thus, you have to change use the following code instead of in the tutorial&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AppDelegate.swift&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AppDelegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIResponder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;UIApplicationDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didFinishLaunchingWithOptions&lt;/span&gt; &lt;span class="nv"&gt;launchOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LaunchOptionsKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]?)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;        
        &lt;span class="c1"&gt;// Just leave it as it, don't change anything&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SceneDelegate.swift&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIScene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;willConnectTo&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UISceneSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="nv"&gt;connectionOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIScene&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ConnectionOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;rootVC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;AppDelegate&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;persistentContainer&lt;/span&gt;

        &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sure confuse the hell out of me, when I just getting start developing iOS app and following the tutorial.&lt;/p&gt;

</description>
      <category>coredata</category>
      <category>swift</category>
      <category>iosdevelopment</category>
    </item>
    <item>
      <title>วิธีการเขียน Clean Code แบบ Uncle Bob</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Fri, 11 Sep 2020 10:05:50 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/clean-code-uncle-bob-1g85</link>
      <guid>https://forem.com/narongdejsrn/clean-code-uncle-bob-1g85</guid>
      <description>&lt;p&gt;การเขียน Code ให้สะอาดนั้นเป็นอะไรที่ยาก ผมเขียนโค้ดมามากกว่า 10 ปี แต่ก็ยังรู้สึกว่าตัวเองเขียน Code ไม่สะอาดเท่าที่ควร ยิ่งเขียน ยิ่งเห็นสิ่งที่แก้ได้ และถ้าต้องกลับไปอ่านโค้ดเก่าๆที่เคยเขียนเมื่อก่อน ผมยอมเขียนใหม่หมดเลยน่าจะดีกว่า&lt;/p&gt;

&lt;p&gt;แต่ความคิดแบบนี้มันไม่ยั่งยืน โดยเฉพาะเมื่อคุณจะต้องทำ Software ที่มีคนใช้งานจริงๆในระยะยาว การที่คุณจะต้องมานั่งเขียนใหม่เรื่อยๆนั่นเป็นไปไม่ได้แน่ๆ และเมื่อคุณต้องทำการทำงานในทีม ถ้าคนอื่นอ่านโค้ดของคุณไม่รู้เรื่อง และไม่สามารถพัฒนาต่อได้ โค้ดของคุณก็เป็นแค่ขยะ&lt;/p&gt;

&lt;p&gt;ตอนนี้ทีมที่บริษัทของผมเรื่มใหญ่ ผมเริ่มเห็น Style การเขียนโค้ดที่แตกต่าง หลายคนสามารถทำโค้ดที่ไม่ซับซ้อนให้มันซับซ้อนได้เพราะวิธีการเขียน ผมเลยสนใจวิธีการเขียนโค้ดให้สะอาด&lt;/p&gt;

&lt;p&gt;และก็ได้ไปเจอ Video ของ Uncle Bob หรือ Robert C. Martin ขณะการทำ Knowledge Sharing ของบริษัท Uncle Bob คือหนึ่งในผู้เขียน Agile Manifesto หรือสิ่งที่เราใช้ Define ว่าอะไรคือ Agile  บล็อกนี้ผมจะมาเขียนสิ่งที่ผมได้เรียนรู้จากการดู Video ของ Uncle Bob&lt;/p&gt;

&lt;h3&gt;
  
  
  WTFs / minute
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Byz67a3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://narongdej.dev/uploads/wtfminute.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Byz67a3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://narongdej.dev/uploads/wtfminute.png" alt="" width="479" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;หากคุณอยากรู้ว่าโค้ดของคุณดีหรือแย่ให้นับจำนวน WTF ตอนทำ Code Review หรือตอนที่คนอื่นอ่านโค้ดของคุณ ถ้าจำนวน WTF น้อย แปลว่าโค้ดของคุณน่าจะค่อนข้างดี ถ้าเยอะก็คงจะอ่านยากแล้ว เพราะฉะนั้นเกณฑ์ในการดูว่าโค้ดของคุณดีรึยัง ให้นับจำนวน WTFs per minute 😂&lt;/p&gt;

&lt;h3&gt;
  
  
  ตั้งชื่อให้เหมาะสม
&lt;/h3&gt;

&lt;p&gt;ชื่อเป็นสิ่งที่สำคัญสุดๆ เราต้องตั้งชื่อ Variable, Function, Argument, Class และอื่นๆอีกมากมาย แน่นอนว่าบางทีคุณก็คิดไม่ออกว่าจะตั้งชื่ออะไร ผมก็เป็น&lt;/p&gt;

&lt;p&gt;ชื่อทุกชื่อ ควรจะบอกว่ามันมีทำไม มีเพื่ออะไร ผมเคยหลาย Project ของคนไทยตั้ง Variable ใน Class Quotation ว่า f1, f2, f3 ไล่ๆไปเรื่อยถึง f10&lt;/p&gt;

&lt;p&gt;การตั้งชื่อ f1 - f10 ไม่ได้บอกอะไรเกี่ยวกับ Variable ตัวนั้นเลย จะดีกว่าไหมถ้าคุณตั้งชื่อเช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;customerName&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;customerAddress&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;customerPhoneNumber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เพราะฉะนั้นจงตั้งชื่อให้สื่อถึงสิ่งที่คุณต้องการให้มันทำ อย่ากลัวว่าชื่อมันจะยาว ตั้งชื่อที่มันเข้าใจง่ายดีกว่า แล้วใช้ IDE ดีๆให้มัน AutoComplete ให้คุณ&lt;/p&gt;

&lt;h3&gt;
  
  
  Functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Function ควรจะสั้น บรรทัดใน Function ควรจะไม่เกิน 170 บรรทัด หาเกินกว่านั้นคุณควรจะแยก Function ให้เป็น โunction ย่อยๆ และตั้งชื่อ function เหล่านั้นให้เข้าใจว่ามันจะทำอะไร&lt;/li&gt;
&lt;li&gt;Block ใน if else, while statement ควรมีแค่หนึ่งบรรทัด หนึ่งบรรทัดนั้นอาจจะเป็น Function call ไปที่ชื่อ Function ที่เข้าใจง่าย&lt;/li&gt;
&lt;li&gt;ควรทำแค่อย่างเดียว ถ้า Function คุณทำหลายอย่าง ให้แยกออกเป็น Function ย่อยๆซะ จนกว่า Function นั่นจะทำแค่อย่างเดียว&lt;/li&gt;
&lt;li&gt;Step-Down Rule เขียน Function ให้สามารถอ่านจากบนลงล่างได้ โดยเรียง Function ย่อยไว้ด้านบนและตามด้วย Function ที่เรียกใช้ Function ก่อนหน้านั้น&lt;/li&gt;
&lt;li&gt;DRY - Don't Repeat Yourself เป็นสิ่งที่ทุกโปรแกรมเมอร์ควรจะรู้ ถ้า Function ทำสื่งที่คล้ายๆกันหรือเหมือนกัน ห้ามก็อปไปวางอีกครั้งเด็กขาด ให้เขียนเป็น Function แทน&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comments
&lt;/h3&gt;

&lt;p&gt;ผมเคยคิดว่าการ Comment เป็นสิ่งที่ดี แล้วเราควรจะเขียน Comment เกือบทุกบรรทัดในการเขียนโค้ด แต่หลังจากลองเขียน Comment จริงๆก็รู้ว่ามันเป็นอะไรที่ซ้ำซาก เช่นการเขียน Comment ให้ Variable ชื่อ &lt;strong&gt;customerName&lt;/strong&gt; การที่เขียน comment เช่น &lt;em&gt;// The name of the customer&lt;/em&gt; มันไม่จำเป็น เพราะชื่อ variable ก็บอกอยู่แล้ว&lt;/p&gt;

&lt;p&gt;Uncle Bob จึงบอกว่าการเขียน Comment เป็นสื่งที่แสดงว่าเราเขียนโค้ดไม่ดี ถ้าเราเขียนโค้ดดี โค้ดควรจะอ่านรู้เรื่องอยู่แล้ว ไม่ว่าจะเป็นเพราะการตั้งชื่อที่ดี การเขียนในสไตล์ที่อ่านไม่ยาก คุณไม่จำเป็นต้องมี Comment เลย เพราะคนที่จะอ่านโค้ดของคุณ ก็ต้องเขียนโปรแกรมเป็นอยู่แล้ว ถ้าเขียนโค้ดดี ก็ไม่จำเป็นต้องมี Comment&lt;/p&gt;

&lt;p&gt;แต่ในบาง case คุณก็ต้องมี comment อยู่ดีเช่น comment license หรือโค้ด ที่เข้าใจได้ยากไม่ว่าจะคุณจะ clean ยังไงก็ตามแล้ว เพราะฉะนั้น ให้ comment เป็นสิ่งสุดท้ายที่คุณทำ ถ้าคุณไม่สามารถทำความสะอาดโค้ดของคุณได้แล้ว&lt;/p&gt;

&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;

&lt;p&gt;การเขียน Test นี่เป็นเรื่องที่ผมไม่ชอบเลยเมื่อก่อน เพราะว่ามันเหมือนเราต้องเขียนโค้ดสองรอบ ทั้งๆที่เรารู้แล้วว่าโค้ดมันเวิร์คอยู่แล้ว แต่นั้นและคือสาเหตุที่เราเขียน Test เพราะว่าเราต้องการ เช็คมันสองรอบ เหมือนว่าทำไมบัญชีต้องจดทั้งรายรับ และรายจ่าย เพื่อที่จะได้รู้ว่าถ้ามันไม่ตรงกัน มันก็ต้องมีอะไรสักอย่างผิดพลาด คุณคงไม่เจอนักบัญชีที่จดแค่รายรับ แต่ไม่จดรายจ่าย&lt;/p&gt;

&lt;p&gt;การเขียน Test ในโปรแกรมก็เหมือนกัน เพราะว่าถ้าเราหยุดเขียน Test เมื่อไหร่ เราไม่มีทางมั่นใจได้เลยว่า โค้ดของเราจะทำงานได้ถูกต้องหรือใหม่&lt;/p&gt;

&lt;p&gt;แล้วเราต้อง Test อะไรบ้างละ? คำตอบคือ ทุกอย่าง เพราะถ้าสมมุติคุณเขียน Test แค่ 80% แปลว่าคุณกำลังบอกทุกคนว่าอีก 20% ของแอพนั้นมีโอกาสที่จะพัง&lt;/p&gt;

&lt;p&gt;Test จะช่วยเยอะมาก หากคุณทำงานใน Team ที่คนอื่นมีโอกาสจะมาแก้ไขโค้ดของคุณ หรือถ้าคุณโค้ดคนเดียว หากคนกลับมาโค้ดโปรเจ็คที่คุณไม่ได้แตะมาแล้วสักหนึ่งปี การมี Test จะช่วยให้คุณมั่นใจว่าสิ่งที่คุณจะแก้จะไม่ทำให้ระบบคุณพัง อีกหนึ่งประโยชน์ของ Test คือ มันคือตัวอย่างการใช้งานระบบของคุณ เพราะถ้าคุณทำ Test ได้ 100% Test นั้นจะเป็นสิ่งที่บอกวิธีการใช้งานทุก Class ทุก Function ในระบบของคุณ โปรแกรมเมอร์แทบทุกคนคงเห็นด้วยว่าการอ่านโค้ดนั้น สะดวกสบายกว่าการไปอ่าน Document ยาวๆ&lt;/p&gt;

&lt;p&gt;การที่จะเขียน Test ให้สำเร็จได้สิ่งที่คุณต้องมีก็คือวินัย ถ้าคุณขาดวินัยในการเขียน Test หรือเรื่ม Comment Test ออกเมื่อไหร่ คุณจะเลิกเขียน Test แน่นอน เพราะฉะนั้นจงเรื่มเขียน Test ให้เร็วและมีวินัย หรือถ้าทำ Test-Driven ได้ยิ่งดี&lt;/p&gt;

&lt;h3&gt;
  
  
  ความสำคัญของ Software
&lt;/h3&gt;

&lt;p&gt;หลายคนๆ แม้กระทั่ง Programmer เองคงไม่ได้ให้ความสำคัญกับโปรแกรมที่ตัวเองเขียนมากเท่าที่ควร หากคุณลองคิดดีๆ ของแทบทุกอย่างควบคุมด้วย Software เช่นรถที่คุณขับ หรือเครื่องบินที่คุณนั่ง ล้วนแต่มีแอพพลิเคชั่นเล็กๆนับพันเพื่อที่มันจะทำงานได้ถูกต้อง หากคุณเขียน Software ที่ไม่ดีและมีบัค แปลว่าคุณมีสิทธิที่จะทำให้คนอื่นเสียชีวิตได้เลยนะ&lt;/p&gt;

&lt;p&gt;สุดท้าย &lt;strong&gt;เขียนโค้ดเหมือนว่าคนที่จะมารับช่วงดูแลโค้ดของคุณต่อเป็นคนโรคจิตหัวรุนแรงที่รู้ว่าบ้านคุณอยู่ไหน&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;หากคุณเป็นสายดูวิดีโอผมแนะนำให้ไปดู &lt;a href="https://www.youtube.com/watch?v=7EmboKQH8lM&amp;amp;list=PLmmYSbUCWJ4x1GO839azG_BBw8rkh-zOj&amp;amp;index=1"&gt;Playlist นี้ของ Uncle Bob&lt;/a&gt; หรือถ้าคุณเป็นสายหนังสือลองอ่านเล่มนี้ดูนะครับ &lt;a href="https://thailand.kinokuniya.com/bw/9780132350884"&gt;Clean Code : A Handbook of Agile Software Craftsmanship (Robert C. Martin)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cleancode</category>
    </item>
    <item>
      <title>Bash Command ที่ควรรู้สำหรับ Data Scientist</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Sat, 23 May 2020 17:00:00 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/bash-command-data-scientist-1b0</link>
      <guid>https://forem.com/narongdejsrn/bash-command-data-scientist-1b0</guid>
      <description>&lt;p&gt;หากคุณเคยใช้ OS อื่นไม่ใช่ Windows คงเคยได้ใช้ Bash กันมาก่อนอย่างแน่นอนไม่มากก็น้อย&lt;/p&gt;

&lt;p&gt;Bash หรือ Bourne Again Shell เป็น default shell ในหลายๆ Linux Distribution ที่เรารู้จักกันดีเช่น Ubuntu กับบน Apple macOS &lt;em&gt;(แต่หลังจาก macOS Catalina ก็เปลี่ยนมาใช้ zsh แทนแล้ว)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;แต่ถึงอย่างนั้นผมก็คิดว่าเราควรเรียน Bash Scripting แบบเบื้องต้นกันไว้บ้าง เพราะมันมากับในหลายๆ OS ซึ่งจะทำให้เราสามารถใช้งานมันได้เลยโดยไม่ต้องลงอะไรเพื่มเลย&lt;/p&gt;

&lt;p&gt;สำหรับการทำงานในสาย Data Scientist หรือ Data Engineer เราต้องทำการอ่าน Data แก้ไข ทำความเข้าไฟล์ตลอด บางทีไฟล์นั้นอาจจะไม่ได้อยู่ในเครื่องของคุณ แต่อยู่ใน Server ที่สามารถเข้าถึงได้โดยใช้ Command Line เท่านั้น มีโอกาสสูงที่โปรแกรมที่คุณเปิดเข้าไปเจอจะเป็น Bash&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. การเปิด Manual
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;man&lt;/code&gt;: หรือ manual เป็นคำสั่งที่ทุกคนควรรู้ มันคือคำสั่งที่เราสามารถพิมพ์ &lt;code&gt;man &amp;lt;ชื่อ command&amp;gt;&lt;/code&gt; เพื่อดู manual สำหรับ command นั้น ถ้าตอนนี้คุณยังไม่รู้จะพิมพ์ command อะไร ลองพิมพ์ &lt;code&gt;man man&lt;/code&gt; เพื่อดู manual ของ command man ก่อนก็ได้&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;กด q เพื่อออกจาก man page&lt;/strong&gt; และสำหรับการ search คุณสามารถพิมพ์ &lt;code&gt;/&amp;lt;สิ่งที่ต้องการ search&amp;gt;&lt;/code&gt; เช่น &lt;code&gt;/whatis&lt;/code&gt; โดยสามารถกด &lt;code&gt;n&lt;/code&gt; เพื่อไปที่ match อันต่อไป หรือกด &lt;code&gt;Shift+n&lt;/code&gt; เพื่อกลับมา match ก่อนหน้า&lt;/p&gt;

&lt;h3&gt;
  
  
  2. การเปลี่ยนและดูข้อมูลของ Working directory
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pwd&lt;/code&gt;: เวลาเราอยากรู้ว่าตอนนี้เราอยู่ที่ directory (folder) ไหน คุณสามารถพิมพ์ &lt;code&gt;pwd&lt;/code&gt; เข้าไปใน bash แล้ว bash ก็จะตอบกลับมาเป็น Full path ของ directory ที่เราอยู่&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash-3.2$ pwd
/Users/narongdejsrn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ในที่นี้ผมอยู่ใน Folder home ของ macOS ซึ่งจะเรื่มต้นด้วย /Users/ ถ้าเป็น Windows ก็จะตอบกลับประมาณ C:\Users&amp;lt;name&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ls&lt;/code&gt;: สำหรับดู​ว่าใน Working directory มีไฟล์อะไรบ้าง คุณสามารถพิมพ์ &lt;code&gt;ls&lt;/code&gt; เพื่อแสดงรายชื่อของไฟล์ สำหรับดูไฟล์ให้ระเอียดมากยิ่งขึ้นสามารถพิมพ์ &lt;code&gt;ls -l&lt;/code&gt; หรือจะพิมพ์ &lt;code&gt;ls -la&lt;/code&gt; หากคุณต้องการแสดงไฟล์ที่เรื่มต้นด้วย . ด้วย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash-3.2$ ls
404.html README.md _pages _sass build mermaid tag_generator.py
CNAME _config.yml _plugins _site favicon.png robots.txt tags.rb
Gemfile _includes _posts assets feed.json styles.scss
Gemfile.lock _layouts _projects atom.xml index.html tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cd&lt;/code&gt;: สำหรับการเปลี่ยน folder คุณสามารถทำการ &lt;code&gt;cd &amp;lt;fullpath&amp;gt;&lt;/code&gt; หรือทำการ cd แบบ relative ได้โดยการทำ &lt;code&gt;cd ./&amp;lt;relative_folder&amp;gt;&lt;/code&gt; หรือ &lt;code&gt;cd ../&lt;/code&gt; เพื่อออกไป folder ก่อนหน้า&lt;/p&gt;

&lt;h3&gt;
  
  
  3. การอ่านและหาข้อมูลในไฟล์
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cat&lt;/code&gt;: สำหรับการเปิดอ่านไฟล์ เช่น &lt;code&gt;cat data.txt&lt;/code&gt; bash ก็จะทำการปรินต์ตัวอักษรใน data.txt ออกมา&lt;/p&gt;

&lt;p&gt;&lt;code&gt;grep&lt;/code&gt;: สำหรับการหาบรรทัดโดยใช้ regular expression สำหรับคนที่ไม่รู้ regular expression ให้ลองไปเล่นที่เว็บไซต์ &lt;a href="https://regex101.com"&gt;regex101&lt;/a&gt; ตัวอย่างเช่นเราต้องการหาบรรทัดที่มีคำว่า Bangkok ในไฟล์ data.txt เราสามารถทำการพิมพ์ &lt;code&gt;cat data.txt | grep "Bangkok"&lt;/code&gt; สิ่งที่เราทำตอนนี้คืออ่าน data จากไฟล์ data.txt ผ่าน command cat และทำการ pipe (การโยนข้อมูล) เข้าไปที่ command grep&lt;/p&gt;

&lt;p&gt;&lt;code&gt;head&lt;/code&gt;: ถ้าเราต้องการอ่านบรรทัดแรกของไฟล์เราสามารถทำการพิมพ์ &lt;code&gt;head -n &amp;lt;จำนวนบรรทัดที่ต้องการอ่าน&amp;gt; &amp;lt;ชื่อไฟล์&amp;gt;&lt;/code&gt; เช่น &lt;code&gt;head -n 5 data.txt&lt;/code&gt; หรือจะ pipe เข้ามาก็ได้ &lt;code&gt;cat data.txt | head -n 5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tail&lt;/code&gt;: เหมือน head ด้านบน แต่อ่านจากบรรทัดล่างสุด&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wc&lt;/code&gt;: นับคำ -w นับบรรทัด -l หรือนับตัวอักษร -m เช่นหากคุณต้องการนับบรรทัดในไฟล์คุณสามารถพิมพ์ &lt;code&gt;wc -l data.txt&lt;/code&gt; หรือจะ pipe เข้ามาเหมือนเดิมก็ได้&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sed&lt;/code&gt;: อ่านและแก้ไขไฟล์โดยใช้ regular expression เช่นหากคุณต้องการเปลี่ยนคำว่า hi เป็น hello ในไฟล์ data คุณสามารถทำการพิมพ์ &lt;code&gt;sed "s/hi/hello/g" data.txt&lt;/code&gt; หลังจากนั้นคุณสามารถ redirect output เข้าไฟล์ได้ด้วยการต่อท้ายด้วย &lt;code&gt;sed "s/hi/hello/g" data.txt &amp;gt; output.txt&lt;/code&gt; หรือจะใช้ -i ก็ได้ &lt;code&gt;sed -i "s/hi/hello/g" data.txt&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;สุดท้าย Bash ยังสามารถทำอะไรได้อีกเยอะ ไว้ว่างๆจะมาเขียนเรื่อง Bash Scripting ต่อนะครับ ระหว่างนี้สามารถเข้าไป Command Bash ทั้งหมดได้ที่&lt;a href="https://courses.cs.washington.edu/courses/cse390a/14au/bash.html"&gt;ลิงค์นี้&lt;/a&gt;ครับ&lt;/p&gt;

</description>
      <category>bash</category>
      <category>datasci</category>
    </item>
    <item>
      <title>Force input validation in Svelte</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Tue, 12 May 2020 17:00:00 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/force-input-validation-in-svelte-3576</link>
      <guid>https://forem.com/narongdejsrn/force-input-validation-in-svelte-3576</guid>
      <description>&lt;p&gt;Svelte! Yet again another new Javascript Framework. Now without virtual dom that React introduce, we go back to a more jQuery style of actual DOM manipulation but still having all that (good?) stuff from React and Vue such as reactivity.&lt;/p&gt;

&lt;p&gt;The website that you will create will likely include a web form. Where the value that the user input needs to be validated somehow. If you search google then you’ll likely come across this &lt;a href="https://svelte.dev/repl/54d159b954d9412c8247807125d9fe1b?version=3.12.1"&gt;repl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It all good and stuff, but what about when you want to reset the validation? For example, when the user submitted a form that locates inside a dialog. When the user presses a button to open that form again, you wouldn’t want those fields to already been validated.&lt;/p&gt;

&lt;p&gt;Here how you can modify the code a bit to allow that&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;validation.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { writable } from "svelte/store"
import { buildValidator } from "./validate"

export function createFieldValidator (...validators) {
  const { subscribe, set } = writable({ dirty: false, valid: false, message: null })
  const validator = buildValidator(validators)

  function action (node, binding) {
    function validate (value, dirty) {
      const result = validator(value, dirty)
      set(result)
    }

    validate(binding, false)

    return {
      update (value) {
        validate(value, true)
      }
    }
  }

  function reset() {
    setTimeout(() =&amp;gt; {
      set({ dirty: false, valid: false, message: null })
    }, 500);
  }

  return [{ subscribe }, action, reset]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can call resetValidation() to reset the validation to false. For example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [validity, validate, resetValidation] = createFieldValidator(requiredValidator(), emailValidator())

resetValidation() // &amp;lt;-- call this to reset the validation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the &lt;a href="https://svelte.dev/repl/83d4c06ff8ed4844b3c3ad150191ca3c?version=3.12.1"&gt;repl&lt;/a&gt;&lt;/p&gt;

</description>
      <category>svelte</category>
    </item>
    <item>
      <title>Create image upload function in Google Firebase</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Sat, 25 Apr 2020 17:00:00 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/create-image-upload-function-in-google-firebase-48ak</link>
      <guid>https://forem.com/narongdejsrn/create-image-upload-function-in-google-firebase-48ak</guid>
      <description>&lt;p&gt;I want to start a new hobby, where I post the solution to something I can’t find 2-3 google away. If I can’t Google and find my solution in one-shot. I’ll write a short tutorial about it. So here comes the first in this blog.&lt;/p&gt;

&lt;p&gt;If you want to create an upload image endpoint inside Google Firebase (Cloud functions), then here is one way you can do it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as functions from 'firebase-functions';
import * as Busboy from 'busboy';
import { tmpdir } from 'os';
import { join } from 'path';
import { createWriteStream, unlinkSync } from 'fs';
import * as FileType from 'file-type';
import * as admin from 'firebase-admin';

admin.initializeApp(); // It will automatically authenticate

export const uploadImage = functions.https.onRequest(async (request, response) =&amp;gt; {
  const busboy = new Busboy({
    headers: request.headers,
    limits: {
      fileSize: 2 * 1024 * 1024, // max image size to 1 MB
      files: 1 // Limit to one file upload
    }
  });

  let filepath: string = "";
  const _tmpdir = tmpdir();

  busboy.on('file', (fieldname, file, filename, encoding, mimetype) =&amp;gt; {
    filepath = join(_tmpdir, filename);
    const writeStream = createWriteStream(filepath);
    file.pipe(writeStream);
  });

  busboy.on('finish', async () =&amp;gt; {
    const fileType = await FileType.fromFile(filepath);
    console.log(fileType);
    if(!fileType || !(fileType.mime === "image/jpeg" || fileType.mime === "image/png")) {
      unlinkSync(filepath);
      response.status(500).send({
        'error': 'Invalid file',
        'code': 'invalid_upload_file'
      });
      return;
    }

  await admin.storage().bucket().upload(filepath, {
    // Support for HTTP requests made with `Accept-Encoding: gzip`
    gzip: true,
    metadata: {
      // Enable long-lived HTTP caching headers
      // Use only if the contents of the file will never change
      // (If the contents will change, use cacheControl: 'no-cache')
      cacheControl: 'public, max-age=31536000',
    },
  });

    response.send({
      'status': 'Upload successful'
    });
  busboy.end(request.rawBody);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;I’m new to Google Firebase and Cloud functions, so this might not be the best way to do it, but it works&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So what we did was&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Process the form data using busboy&lt;/li&gt;
&lt;li&gt;Store the image in a temp directory&lt;/li&gt;
&lt;li&gt;Upload the image using &lt;code&gt;firebase-admin&lt;/code&gt; storage module&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  But what about multiple files upload at once?
&lt;/h2&gt;

&lt;p&gt;You can modify this line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    limits: {
      ...
      files: 50 // &amp;lt;--- or remove it to allow infinity
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and instead of replacing &lt;code&gt;filepath&lt;/code&gt; you create an array of file paths instead. After that, process the &lt;code&gt;filepaths&lt;/code&gt; array accordingly in&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; busboy.on('finish', async () =&amp;gt; {
   ...process the array
 });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>firebase</category>
      <category>node</category>
    </item>
    <item>
      <title>Upgrading PHP to 7.3 with Ansible</title>
      <dc:creator>Narongdej Sarnsuwan</dc:creator>
      <pubDate>Mon, 03 Jun 2019 13:42:51 +0000</pubDate>
      <link>https://forem.com/narongdejsrn/upgrading-php-to-7-3-with-ansible-4fp1</link>
      <guid>https://forem.com/narongdejsrn/upgrading-php-to-7-3-with-ansible-4fp1</guid>
      <description>&lt;p&gt;At my company, one of our services is to develop and maintain Wordpress websites for our clients. We host our clients' website using Digitalocean and we want to separate customer environment easily so we use different droplet for each customer. However, the pre-installed version from the DigitalOcean image is 7.0.X and it was deprecated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save by Ansible
&lt;/h2&gt;

&lt;p&gt;Ansible is an IT Automation tool by Redhat which lets you automate IT tasks with ease. And did I mention, &lt;strong&gt;its free&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;While you can do a load of things using Ansible, we will only focus on upgrading PHP on our servers. Be sure to check out what you can do with ansible afterward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;First, you gotta install Ansible control node. On a bigger scale, this would be on a server who manages other ansible nodes (other servers) through SSH. However, for our case; we will use our own computer as a control node.&lt;/p&gt;

&lt;p&gt;Ansible works on any machine that can run Python 2 or 3, &lt;strong&gt;except for Windows&lt;/strong&gt;. Use the Linux subsystem or something else if you are running Windows.&lt;/p&gt;

&lt;p&gt;For Mac users, the preferred way is to install ansible using pip&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using Ubuntu 16.04 or 18.04 run the command below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;software-properties-common
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-add-repository &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="nt"&gt;--update&lt;/span&gt; ppa:ansible/ansible
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For other distros or OS, check out &lt;a href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-the-control-node"&gt;the official installation tutorial&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup your inventory
&lt;/h2&gt;

&lt;p&gt;In Ansible, you identify the location of your servers in the inventory file located at &lt;code&gt;/etc/ansible/hosts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create your inventory file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[wordpress]
website-a ansible_host=122.X.X.X ansible_port=2222 ansible_user=someuser
website-b ansible_host=122.X.X.X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, you created a hosts group called &lt;em&gt;wordpress&lt;/em&gt; which contains website-a and website-b&lt;/p&gt;

&lt;p&gt;You can also set &lt;em&gt;ansible_password=&lt;/em&gt; but I recommended you to set up the public key&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your playbook
&lt;/h2&gt;

&lt;p&gt;Next up we'll create an orchestration file to tell ansible what to do. Create a file name &lt;code&gt;upgrade-php.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;php_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;7.3'&lt;/span&gt;
    &lt;span class="na"&gt;php_versions_install_recommends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;geerlingguy.repo-remi&lt;/span&gt;
      &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ansible_os_family == 'RedHat'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;geerlingguy.php-versions&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;geerlingguy.php&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happening here is we're telling ansible to run set of tasks (roles) geerlingguy.php-versions, and geerlingguy.php.&lt;/p&gt;

&lt;p&gt;Just like NPM or pip, you need to download the package from an online repo in order to use it. Run these commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-galaxy &lt;span class="nb"&gt;install &lt;/span&gt;geerlingguy.repo-remi
ansible-galaxy &lt;span class="nb"&gt;install &lt;/span&gt;geerlingguy.php-versions
ansible-galaxy &lt;span class="nb"&gt;install &lt;/span&gt;geerlingguy.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This awesome role by &lt;a href="https://github.com/geerlingguy"&gt;geerlingguy&lt;/a&gt; purge old PHP versions, add the appropriate repository, install your PHP and install all the recommendations modules. You can review what the role does &lt;a href="https://github.com/geerlingguy/ansible-role-php-versions/blob/master/tasks/main.yml"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade the server PHP
&lt;/h2&gt;

&lt;p&gt;Finally, we are ready to upgrade PHP on our servers. Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-playbook upgrade-php.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the playbook to finish and your PHP version should be upgraded to 7.3 🎉🎉🎉&lt;/p&gt;

&lt;p&gt;Ansible has a lot more awesome features that make your sysadmin mundane tasks go away in one command. I'm new to ansible and wish to have known about it sooner. If you have any cool ansible project, then please tell me about it in the comment. &lt;/p&gt;

</description>
      <category>devops</category>
      <category>php</category>
      <category>ansible</category>
      <category>sysadmin</category>
    </item>
  </channel>
</rss>
